From 64fb5f2ee13502ba33894be3c2ad775c0231a2d0 Mon Sep 17 00:00:00 2001 From: "jws@cairnwell.research" Date: Mon, 31 Mar 2003 11:34:23 +0000 Subject: [PATCH] bitkeeper revision 1.161 (3e8827bfgvKhhIcTx5141jdvnhh7gg) megaraid support (beta) new include file version.h makes porting easier --- .rootkeys | 3 + xen/drivers/scsi/megaraid.c | 5534 +++++++++++++++++++++++++++++++++++ xen/drivers/scsi/megaraid.h | 1040 +++++++ xen/include/xeno/version.h | 2 + 4 files changed, 6579 insertions(+) create mode 100644 xen/drivers/scsi/megaraid.c create mode 100644 xen/drivers/scsi/megaraid.h create mode 100644 xen/include/xeno/version.h diff --git a/.rootkeys b/.rootkeys index 5cfbe6a9aa..746275c4d2 100644 --- a/.rootkeys +++ b/.rootkeys @@ -191,6 +191,8 @@ 3ddb79beXZxwKh7cGyPfr40bhDyRrA xen/drivers/scsi/constants.h 3e564120ZeinH9nf3IVSerB80T7dHg xen/drivers/scsi/hosts.c 3ddb79beGiGljlTNq_kRnCBZECgC9Q xen/drivers/scsi/hosts.h +3e8827bdE8FgsI-QxZ4zKzYTRmACOQ xen/drivers/scsi/megaraid.c +3e8827bdEMIzjyF9OHnDRmJGp_O68g xen/drivers/scsi/megaraid.h 3ddb79bexarQo1tQ541PPUyK9HXNDA xen/drivers/scsi/scsi.c 3ddb79beBOiYxQUiWTHosepRlJyuGA xen/drivers/scsi/scsi.h 3ddb79beVTYJj6_KMxYLJmCP7p9MuQ xen/drivers/scsi/scsi_dma.c @@ -331,6 +333,7 @@ 3ddb79c2_m8lT9jDKse_tePj7zcnNQ xen/include/xeno/timex.h 3ddb79c2e2C14HkndNEJlYwXaPrF5A xen/include/xeno/tqueue.h 3ddb79c1-kVvF8cVa0k3ZHDdBMj01Q xen/include/xeno/types.h +3e8827bdaqPeZAWGVOwswgY9bWSx4g xen/include/xeno/version.h 3ddb79c2Ae5KpzhC9LCYG7mP_Vi4Aw xen/include/xeno/vif.h 3ddb79c4YQCQ6r0xNLLu0jfbM7pVmA xen/net/Makefile 3ddb79c4AkfDkTCw0comx4L8wsUOMg xen/net/dev.c diff --git a/xen/drivers/scsi/megaraid.c b/xen/drivers/scsi/megaraid.c new file mode 100644 index 0000000000..26c25a197a --- /dev/null +++ b/xen/drivers/scsi/megaraid.c @@ -0,0 +1,5534 @@ +/*=================================================================== + * + * Linux MegaRAID device driver + * + * Copyright 2001 LSI Logic Corporation. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version + * 2 of the License, or (at your option) any later version. + * + * Version : v1.18d (Aug 7, 2002) + * + * Description: Linux device driver for LSI Logic MegaRAID controller + * + * Supported controllers: MegaRAID 418, 428, 438, 466, 762, 467, 471, 490 + * 493. + * History: + * + * Version 0.90: + * Original source contributed by Dell; integrated it into the kernel and + * cleaned up some things. Added support for 438/466 controllers. + * Version 0.91: + * Aligned mailbox area on 16-byte boundary. + * Added schedule() at the end to properly clean up. + * Made improvements for conformity to linux driver standards. + * + * Version 0.92: + * Added support for 2.1 kernels. + * Reads from pci_dev struct, so it's not dependent on pcibios. + * Added some missing virt_to_bus() translations. + * Added support for SMP. + * Changed global cli()'s to spinlocks for 2.1, and simulated + * spinlocks for 2.0. + * Removed setting of SA_INTERRUPT flag when requesting Irq. + * + * Version 0.92ac: + * Small changes to the comments/formatting. Plus a couple of + * added notes. Returned to the authors. No actual code changes + * save printk levels. + * 8 Oct 98 Alan Cox + * + * Merged with 2.1.131 source tree. + * 12 Dec 98 K. Baranowski + * + * Version 0.93: + * Added support for vendor specific ioctl commands (M_RD_IOCTL_CMD+xxh) + * Changed some fields in MEGARAID struct to better values. + * Added signature check for Rp controllers under 2.0 kernels + * Changed busy-wait loop to be time-based + * Fixed SMP race condition in isr + * Added kfree (sgList) on release + * Added #include linux/version.h to megaraid.h for hosts.h + * Changed max_id to represent max logical drives instead of targets. + * + * Version 0.94: + * Got rid of some excess locking/unlocking + * Fixed slight memory corruption problem while memcpy'ing into mailbox + * Changed logical drives to be reported as luns rather than targets + * Changed max_id to 16 since it is now max targets/chan again. + * Improved ioctl interface for upcoming megamgr + * + * Version 0.95: + * Fixed problem of queueing multiple commands to adapter; + * still has some strange problems on some setups, so still + * defaults to single. To enable parallel commands change + * #define MULTI_IO in megaraid.h + * Changed kmalloc allocation to be done in beginning. + * Got rid of C++ style comments + * + * Version 0.96: + * 762 fully supported. + * + * Version 0.97: + * Changed megaraid_command to use wait_queue. + * + * Version 1.00: + * Checks to see if an irq occurred while in isr, and runs through + * routine again. + * Copies mailbox to temp area before processing in isr + * Added barrier() in busy wait to fix volatility bug + * Uses separate list for freed Scbs, keeps track of cmd state + * Put spinlocks around entire queue function for now... + * Full multi-io commands working stablely without previous problems + * Added skipXX LILO option for Madrona motherboard support + * + * Version 1.01: + * Fixed bug in mega_cmd_done() for megamgr control commands, + * the host_byte in the result code from the scsi request to + * scsi midlayer is set to DID_BAD_TARGET when adapter's + * returned codes are 0xF0 and 0xF4. + * + * Version 1.02: + * Fixed the tape drive bug by extending the adapter timeout value + * for passthrough command to 60 seconds in mega_build_cmd(). + * + * Version 1.03: + * Fixed Madrona support. + * Changed the adapter timeout value from 60 sec in 1.02 to 10 min + * for bigger and slower tape drive. + * Added driver version printout at driver loadup time + * + * Version 1.04 + * Added code for 40 ld FW support. + * Added new ioctl command 0x81 to support NEW_READ/WRITE_CONFIG with + * data area greater than 4 KB, which is the upper bound for data + * tranfer through scsi_ioctl interface. + * The additional 32 bit field for 64bit address in the newly defined + * mailbox64 structure is set to 0 at this point. + * + * Version 1.05 + * Changed the queing implementation for handling SCBs and completed + * commands. + * Added spinlocks in the interrupt service routine to enable the driver + * function in the SMP environment. + * Fixed the problem of unnecessary aborts in the abort entry point, which + * also enables the driver to handle large amount of I/O requests for + * long duration of time. + * Version 1.06 + * Intel Release + * Version 1.07 + * Removed the usage of uaccess.h file for kernel versions less than + * 2.0.36, as this file is not present in those versions. + * + * Version 108 + * Modified mega_ioctl so that 40LD megamanager would run + * Made some changes for 2.3.XX compilation , esp wait structures + * Code merge between 1.05 and 1.06 . + * Bug fixed problem with ioctl interface for concurrency between + * 8ld and 40ld firwmare + * Removed the flawed semaphore logic for handling new config command + * Added support for building own scatter / gather list for big user + * mode buffers + * Added /proc file system support ,so that information is available in + * human readable format + * + * Version 1a08 + * Changes for IA64 kernels. Checked for CONFIG_PROC_FS flag + * + * Version 1b08 + * Include file changes. + * Version 1b08b + * Change PCI ID value for the 471 card, use #defines when searching + * for megaraid cards. + * + * Version 1.10 + * + * I) Changes made to make following ioctl commands work in 0x81 interface + * a)DCMD_DELETE_LOGDRV + * b)DCMD_GET_DISK_CONFIG + * c)DCMD_DELETE_DRIVEGROUP + * d)NC_SUBOP_ENQUIRY3 + * e)DCMD_CHANGE_LDNO + * f)DCMD_CHANGE_LOOPID + * g)DCMD_FC_READ_NVRAM_CONFIG + * h)DCMD_WRITE_CONFIG + * II) Added mega_build_kernel_sg function + * III)Firmware flashing option added + * + * Version 1.10a + * + * I)Dell updates included in the source code. + * Note: This change is not tested due to the unavailability of IA64 kernel + * and it is in the #ifdef DELL_MODIFICATION macro which is not defined + * + * Version 1.10b + * + * I)In M_RD_IOCTL_CMD_NEW command the wrong way of copying the data + * to the user address corrected + * + * Version 1.10c + * + * I) DCMD_GET_DISK_CONFIG opcode updated for the firmware changes. + * + * Version 1.11 + * I) Version number changed from 1.10c to 1.11 + * II) DCMD_WRITE_CONFIG(0x0D) command in the driver changed from + * scatter/gather list mode to direct pointer mode.. + * Fixed bug of undesirably detecting HP onboard controllers which + * are disabled. + * + * Version 1.12 (Sep 21, 2000) + * + * I. Changes have been made for Dynamic DMA mapping in IA64 platform. + * To enable all these changes define M_RD_DYNAMIC_DMA_SUPPORT in megaraid.h + * II. Got rid of windows mode comments + * III. Removed unwanted code segments + * IV. Fixed bug of HP onboard controller information (commented with + * MEGA_HP_FIX) + * + * Version 1a12 + * I. reboot notifier and new ioctl changes ported from 1c09 + * + * Version 1b12 + * I. Changes in new ioctl interface routines ( Nov 06, 2000 ) + * + * Version 1c12 + * I. Changes in new ioctl interface routines ( Nov 07, 2000 ) + * + * Version 1d12 + * I. Compilation error under kernel 2.4.0 for 32-bit machine in mega_ioctl + * + * Version 1e12, 1f12 + * 1. Fixes for pci_map_single, pci_alloc_consistent along with mailbox + * alignment + * + * Version 1.13beta + * Added Support for Full 64bit address space support. If firmware + * supports 64bit, it goes to 64 bit mode even on x86 32bit + * systems. Data Corruption Issues while running on test9 kernel + * on IA64 systems. This issue not seen on test11 on x86 system + * + * Version 1.13c + * 1. Resolved Memory Leak when using M_RD_IOCTL_CMD interface + * 2. Resolved Queuing problem when MailBox Blocks + * 3. Added unregister_reboot_notifier support + * + * Version 1.13d + * Experimental changes in interfacing with the controller in ISR + * + * Version 1.13e + * Fixed Broken 2.2.XX compilation changes + misc changes + * + * Version 1.13f to 1.13i + * misc changes + code clean up + * Cleaned up the ioctl code and added set_mbox_xfer_addr() + * Support for START_DEV (6) + * + * Version 1.13j + * Moved some code to megaraid.h file, replaced some hard coded values + * with respective macros. Changed some functions to static + * + * Version 1.13k + * Only some idendation correction to 1.13j + * + * Version 1.13l , 1.13m, 1.13n, 1.13o + * Minor Identation changes + misc changes + * + * Version 1.13q + * Paded the new uioctl_t MIMD structure for maintaining alignment + * and size across 32 / 64 bit platforms + * Changed the way MIMD IOCTL interface used virt_to_bus() to use pci + * memory location + * + * Version 1.13r + * 2.4.xx SCSI Changes. + * + * Version 1.13s + * Stats counter fixes + * Temporary fix for some 64 bit firmwares in 2.4.XX kernels + * + * Version 1.13t + * Support for 64bit version of READ/WRITE/VIEW DISK CONFIG + * + * Version 1.14 + * Did away with MEGADEV_IOCTL flag. It is now standard part of driver + * without need for a special #define flag + * Disabled old scsi ioctl path for kernel versions > 2.3.xx. This is due + * to the nature in which the new scsi code queues a new scsi command to + * controller during SCSI IO Completion + * Driver now checks for sub-system vendor id before taking ownership of + * the controller + * + * Version 1.14a + * Added Host re-ordering + * + * Version 1.14b + * Corrected some issue which caused the older cards not to work + * + * Version 1.14c + * IOCTL changes for not handling the non-64bit firmwares under 2.4.XX + * kernel + * + * Version 1.14d + * Fixed Various MIMD Synchronization Issues + * + * Version 1.14e + * Fixed the error handling during card initialization + * + * Version 1.14f + * Multiple invocations of mimd phase I ioctl stalls the cpu. Replaced + * spinlock with semaphore(mutex) + * + * Version 1.14g + * Fixed running out of scbs issues while running MIMD apps under heavy IO + * + * Version 1.14g-ac - 02/03/01 + * Reformatted to Linux format so I could compare to old one and cross + * check bug fixes + * Re fixed the assorted missing 'static' cases + * Removed some unneeded version checks + * Cleaned up some of the VERSION checks in the code + * Left 2.0 support but removed 2.1.x support. + * Collected much of the compat glue into one spot + * + * Version 1.14g-ac2 - 22/03/01 + * Fixed a non obvious dereference after free in the driver unload path + * + * Version 1.14i + * changes for making 32bit application run on IA64 + * + * Version 1.14j + * Tue Mar 13 14:27:54 EST 2001 - AM + * Changes made in the driver to be able to run applications if the + * system has memory >4GB. + * + * + * Version 1.14k + * Thu Mar 15 18:38:11 EST 2001 - AM + * + * Firmware version check removed if subsysid==0x1111 and + * subsysvid==0x1111, since its not yet initialized. + * + * changes made to correctly calculate the base in mega_findCard. + * + * Driver informational messages now appear on the console as well as + * with dmesg + * + * Older ioctl interface is returned failure on newer(2.4.xx) kernels. + * + * Inclusion of "modversions.h" is still a debatable question. It is + * included anyway with this release. + * + * Version 1.14l + * Mon Mar 19 17:39:46 EST 2001 - AM + * + * Assorted changes to remove compilation error in 1.14k when compiled + * with kernel < 2.4.0 + * + * Version 1.14m + * Tue Mar 27 12:09:22 EST 2001 - AM + * + * Added support for extended CDBs ( > 10 bytes ) and OBDR ( One Button + * Disaster Recovery ) feature. + * + * + * Version 1.14n + * Tue Apr 10 14:28:13 EDT 2001 - AM + * + * "modeversions.h" is no longer included in the code. + * 2.4.xx style mutex initialization used for older kernels also + * + * Version 1.14o + * Wed Apr 18 17:47:26 EDT 2001 - PJ + * + * Before returning status for 'inquiry', we first check if request buffer + * is SG list, and then return appropriate status + * + * Version 1.14p + * Wed Apr 25 13:44:48 EDT 2001 - PJ + * + * SCSI result made appropriate in case of check conditions for extended + * passthru commands + * + * Do not support lun >7 for physically accessed devices + * + * + * Version 1.15 + * Thu Apr 19 09:38:38 EDT 2001 - AM + * + * 1.14l rollover to 1.15 - merged with main trunk after 1.15d + * + * Version 1.15b + * Wed May 16 20:10:01 EDT 2001 - AM + * + * "modeversions.h" is no longer included in the code. + * 2.4.xx style mutex initialization used for older kernels also + * Brought in-sync with Alan's changes in 2.4.4 + * Note: 1.15a is on OBDR branch(main trunk), and is not merged with yet. + * + * Version 1.15c + * Mon May 21 23:10:42 EDT 2001 - AM + * + * ioctl interface uses 2.4.x conforming pci dma calls + * similar calls used for older kernels + * + * Version 1.15d + * Wed May 30 17:30:41 EDT 2001 - AM + * + * NULL is not a valid first argument for pci_alloc_consistent() on + * IA64(2.4.3-2.10.1). Code shuffling done in ioctl interface to get + * "pci_dev" before making calls to pci interface routines. + * + * Version 1.16pre + * Fri Jun 1 19:40:48 EDT 2001 - AM + * + * 1.14p and 1.15d merged + * ROMB support added + * + * Version 1.16-pre1 + * Mon Jun 4 15:01:01 EDT 2001 - AM + * + * Non-ROMB firmware do no DMA support 0xA9 command. Value 0xFF + * (all channels are raid ) is chosen for those firmware. + * + * Version 1.16-pre2 + * Mon Jun 11 18:15:31 EDT 2001 - AM + * + * Changes for boot from any logical drive + * + * Version 1.16 + * Tue Jun 26 18:07:02 EDT 2001 - AM + * + * branched at 1.14p + * + * Check added for HP 1M/2M controllers if having firmware H.01.07 or + * H.01.08. If found, disable 64 bit support since these firmware have + * limitations for 64 bit addressing + * + * + * Version 1.17 + * Thu Jul 12 11:14:09 EDT 2001 - AM + * + * 1.16pre2 and 1.16 merged. + * + * init_MUTEX and init_MUTEX_LOCKED are defined in 2.2.19. Pre-processor + * statements are added for them + * + * Linus's 2.4.7pre3 kernel introduces a new field 'max_sectors' in Scsi_Host + * structure, to improve IO performance. + * + * + * Version 1.17a + * Fri Jul 13 18:44:01 EDT 2001 - AM + * + * Starting from kernel 2.4.x, LUN is not < 8 - following SCSI-III. So to have + * our current formula working to calculate logical drive number, return + * failure for LUN > 7 + * + * + * Version 1.17b + * Mon Jul 30 19:24:02 EDT 2001 - AM + * + * Added support for random deletion of logical drives + * + * Version 1.17c + * Tue Sep 25 09:37:49 EDT 2001 - Atul Mukker + * + * With single and dual channel controllers, some virtaul channels are + * displayed negative. + * + * Version 1.17a-ac + * Mon Aug 6 14:59:29 BST 2001 - "Michael Johnson" + * + * Make the HP print formatting and check for buggy firmware runtime not + * ifdef dependant. + * + * + * Version 1.17d + * Thu Oct 11 10:48:45 EDT 2001 - Atul Mukker + * + * Driver 1.17c oops when loaded without controller. + * + * Special case for "use_sg == 1" removed while building the scatter gather + * list. + * + * Version 1.18 + * Thu Oct 11 15:02:53 EDT 2001 - Atul Mukker + * + * References to AMI have been changed to LSI Logic. + * + * Version 1.18a + * Mon Mar 11 11:38:38 EST 2002 - Atul Mukker + * + * RAID On MotherBoard (ROMB) - boot from logical or physical drives + * + * Support added for discovery(ROMB) vendor and device ids. + * + * Data transfer length for passthru commands must be valid even if the + * command has an associated scatter-gather list. + * + * + * Version 1.18b + * Tue Apr 23 11:01:58 EDT 2002 - Atul Mukker + * + * typo corrected for scsi condition CHECK_CONDITION in mega_cmd_done() + * + * Support added for PCI_VENDOR_ID_LSI_LOGIC with device id + * PCI_DEVICE_ID_AMI_MEGARAID3. + * + * + * Version 1.18c + * Thu May 16 10:27:55 EDT 2002 - Atul Mukker + * + * Retrun valid return status for mega passthru commands resulting in + * contingent allegiance condition. Check for 64-bit passthru commands also. + * + * Do not check_region() anymore and check for return value of + * request_region() + * + * Send valid sense data to appliations using the private ioctl interface. + * + * + * Version 1.18d + * Wed Aug 7 18:51:51 EDT 2002 - Atul Mukker + * + * Added support for yellowstone and verde controller + * + * + * BUGS: + * Some older 2.1 kernels (eg. 2.1.90) have a bug in pci.c that + * fails to detect the controller as a pci device on the system. + * + * Timeout period for upper scsi layer, i.e. SD_TIMEOUT in + * /drivers/scsi/sd.c, is too short for this controller. SD_TIMEOUT + * value must be increased to (30 * HZ) otherwise false timeouts + * will occur in the upper layer. + * + * Never set skip_id. The existing PCI code the megaraid uses fails + * to properly check the vendor subid in some cases. Setting this then + * makes it steal other i960's and crashes some boxes + * + * Far too many ifdefs for versions. + * + *===================================================================*/ + + +#include +#include +#include +#include +#include +#include +#include +#include +#include +//#include +#include +#include +//#include +#include +//#include +#include +#include +#include +//#include + +#include +#include +//#include +#include /* for kmalloc() */ +#if LINUX_VERSION_CODE < KERNEL_VERSION(2,1,0) /* 0x20100 */ +#include +#else +#if LINUX_VERSION_CODE < KERNEL_VERSION(2,3,0) /* 0x20300 */ +#include +#else +#include +#endif +#endif + +#include +#include + +#if LINUX_VERSION_CODE > KERNEL_VERSION(2,0,24) /* 0x020024 */ +#include +#endif + +/* + * These header files are required for Shutdown Notification routines + */ +#include +#include +#include + +#include "sd.h" +#include "scsi.h" +#include "hosts.h" + +#include "megaraid.h" + +/* + *================================================================ + * #Defines + *================================================================ + */ + +#define MAX_SERBUF 160 +#define COM_BASE 0x2f8 + +static ulong RDINDOOR (mega_host_config * megaCfg) +{ + return readl (megaCfg->base + 0x20); +} + +static void WRINDOOR (mega_host_config * megaCfg, ulong value) +{ + writel (value, megaCfg->base + 0x20); +} + +static ulong RDOUTDOOR (mega_host_config * megaCfg) +{ + return readl (megaCfg->base + 0x2C); +} + +static void WROUTDOOR (mega_host_config * megaCfg, ulong value) +{ + writel (value, megaCfg->base + 0x2C); +} + +#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,2,0) /* 0x020200 */ +#include +#define cpuid smp_processor_id() +#endif + +#if LINUX_VERSION_CODE < KERNEL_VERSION(2,4,4) +#define scsi_set_pci_device(x,y) +#endif + +#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,4,0) /* 0x020400 */ + +/* + * Linux 2.4 and higher + * + * No driver private lock + * Use the io_request_lock not cli/sti + * queue task is a simple api without irq forms + */ + +MODULE_AUTHOR ("LSI Logic Corporation"); +MODULE_DESCRIPTION ("LSI Logic MegaRAID driver"); +MODULE_LICENSE ("GPL"); + +#define DRIVER_LOCK_T +#define DRIVER_LOCK_INIT(p) +#define DRIVER_LOCK(p) +#define DRIVER_UNLOCK(p) +#define IO_LOCK_T unsigned long io_flags = 0 +#define IO_LOCK spin_lock_irqsave(&io_request_lock,io_flags); +#define IO_UNLOCK spin_unlock_irqrestore(&io_request_lock,io_flags); + +#define queue_task_irq(a,b) queue_task(a,b) +#define queue_task_irq_off(a,b) queue_task(a,b) + +#elif LINUX_VERSION_CODE >= KERNEL_VERSION(2,2,0) /* 0x020200 */ + +/* + * Linux 2.2 and higher + * + * No driver private lock + * Use the io_request_lock not cli/sti + * No pci region api + * queue_task is now a single simple API + */ + +static char kernel_version[] = UTS_RELEASE; +MODULE_AUTHOR ("LSI Logic Corporation"); +MODULE_DESCRIPTION ("LSI Logic MegaRAID driver"); + +#define DRIVER_LOCK_T +#define DRIVER_LOCK_INIT(p) +#define DRIVER_LOCK(p) +#define DRIVER_UNLOCK(p) +#define IO_LOCK_T unsigned long io_flags = 0 +#define IO_LOCK spin_lock_irqsave(&io_request_lock,io_flags); +#define IO_UNLOCK spin_unlock_irqrestore(&io_request_lock,io_flags); + +#define pci_free_consistent(a,b,c,d) +#define pci_unmap_single(a,b,c,d) +#define pci_enable_device(x) (0) +#define queue_task_irq(a,b) queue_task(a,b) +#define queue_task_irq_off(a,b) queue_task(a,b) + +#if LINUX_VERSION_CODE < KERNEL_VERSION(2,2,19) /* 0x020219 */ +#define init_MUTEX_LOCKED(x) (*(x)=MUTEX_LOCKED) +#define init_MUTEX(x) (*(x)=MUTEX) +#define DECLARE_WAIT_QUEUE_HEAD(x) struct wait_queue *x = NULL +#endif + + +#else + +/* + * Linux 2.0 macros. Here we have to provide some of our own + * functionality. We also only work little endian 32bit. + * Again no pci_alloc/free api + * IO_LOCK/IO_LOCK_T were never used in 2.0 so now are empty + */ + +#define cpuid 0 +#define DRIVER_LOCK_T long cpu_flags; +#define DRIVER_LOCK_INIT(p) +#define DRIVER_LOCK(p) \ + save_flags(cpu_flags); \ + cli(); +#define DRIVER_UNLOCK(p) \ + restore_flags(cpu_flags); +#define IO_LOCK_T +#define IO_LOCK(p) +#define IO_UNLOCK(p) +#define le32_to_cpu(x) (x) +#define cpu_to_le32(x) (x) + +#define pci_free_consistent(a,b,c,d) +#define pci_unmap_single(a,b,c,d) + +#define init_MUTEX_LOCKED(x) (*(x)=MUTEX_LOCKED) +#define init_MUTEX(x) (*(x)=MUTEX) + +#define pci_enable_device(x) (0) + +/* + * 2.0 lacks spinlocks, iounmap/ioremap + */ + +#define ioremap vremap +#define iounmap vfree + + /* simulate spin locks */ +typedef struct { + volatile char lock; +} spinlock_t; + +#define spin_lock_init(x) { (x)->lock = 0;} +#define spin_lock_irqsave(x,flags) { while ((x)->lock) barrier();\ + (x)->lock=1; save_flags(flags);\ + cli();} +#define spin_unlock_irqrestore(x,flags) { (x)->lock=0; restore_flags(flags);} + +#define DECLARE_WAIT_QUEUE_HEAD(x) struct wait_queue *x = NULL + +#endif + + +#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,4,0) /* 0x020400 */ +#define dma_alloc_consistent pci_alloc_consistent +#define dma_free_consistent pci_free_consistent +#else +#if LINUX_VERSION_CODE < KERNEL_VERSION(2,2,19) /* 0x020219 */ +typedef unsigned long dma_addr_t; +#endif +void *dma_alloc_consistent(void *, size_t, dma_addr_t *); +void dma_free_consistent(void *, size_t, void *, dma_addr_t); +int mega_get_order(int); +int pow_2(int); +#endif + +/* set SERDEBUG to 1 to enable serial debugging */ +#define SERDEBUG 0 +#if SERDEBUG +static void ser_init (void); +static void ser_puts (char *str); +static void ser_putc (char c); +static int ser_printk (const char *fmt, ...); +#endif + +#ifdef CONFIG_PROC_FS +#define COPY_BACK if (offset > megaCfg->procidx) { \ + *eof = TRUE; \ + megaCfg->procidx = 0; \ + megaCfg->procbuf[0] = 0; \ + return 0;} \ + if ((count + offset) > megaCfg->procidx) { \ + count = megaCfg->procidx - offset; \ + *eof = TRUE; } \ + memcpy(page, &megaCfg->procbuf[offset], count); \ + megaCfg->procidx = 0; \ + megaCfg->procbuf[0] = 0; +#endif + +/* + * ================================================================ + * Global variables + *================================================================ + */ + +/* Use "megaraid=skipXX" as LILO option to prohibit driver from scanning + XX scsi id on each channel. Used for Madrona motherboard, where SAF_TE + processor id cannot be scanned */ + +static char *megaraid; +#if LINUX_VERSION_CODE > KERNEL_VERSION(2,1,0) /* 0x20100 */ +#ifdef MODULE +MODULE_PARM (megaraid, "s"); +#endif +#endif +static int skip_id = -1; +static int numCtlrs = 0; +static mega_host_config *megaCtlrs[FC_MAX_CHANNELS] = { 0 }; +#if XENO_KILLED +static struct proc_dir_entry *mega_proc_dir_entry; +#endif + +#if DEBUG +static u32 maxCmdTime = 0; +#endif + +static mega_scb *pLastScb = NULL; +#if XENO_KILLED +static struct notifier_block mega_notifier = { + megaraid_reboot_notify, + NULL, + 0 +}; +#endif + +/* For controller re-ordering */ +struct mega_hbas mega_hbas[MAX_CONTROLLERS]; + +/* + * The File Operations structure for the serial/ioctl interface of the driver + */ +/* For controller re-ordering */ +#if XENO_KILLED +static struct file_operations megadev_fops = { + ioctl:megadev_ioctl_entry, + open:megadev_open, + release:megadev_close, +}; +#endif +/* + * Array to structures for storing the information about the controllers. This + * information is sent to the user level applications, when they do an ioctl + * for this information. + */ +static struct mcontroller mcontroller[MAX_CONTROLLERS]; + +/* The current driver version */ +static u32 driver_ver = 0x118C; + +#if XENO_KILLED +/* major number used by the device for character interface */ +static int major; + +static struct semaphore mimd_ioctl_sem; +static struct semaphore mimd_entry_mtx; +#endif + +#if SERDEBUG +volatile static spinlock_t serial_lock; +#endif + +#if LINUX_VERSION_CODE < KERNEL_VERSION(2,4,0) /* 0x20300 */ +static struct proc_dir_entry proc_scsi_megaraid = { + PROC_SCSI_MEGARAID, 8, "megaraid", + S_IFDIR | S_IRUGO | S_IXUGO, 2 +}; +#endif + +#ifdef CONFIG_PROC_FS +extern struct proc_dir_entry proc_root; +#endif + +#define IS_RAID_CH(this, ch) ( (this->mega_ch_class >> (ch)) & 0x01 ) + +#if SERDEBUG +static char strbuf[MAX_SERBUF + 1]; + +static void ser_init (void) +{ + unsigned port = COM_BASE; + + outb (0x80, port + 3); + outb (0, port + 1); + /* 9600 Baud, if 19200: outb(6,port) */ + outb (12, port); + outb (3, port + 3); + outb (0, port + 1); +} + +static void ser_puts (char *str) +{ + char *ptr; + + ser_init (); + for (ptr = str; *ptr; ++ptr) + ser_putc (*ptr); +} + +static void ser_putc (char c) +{ + unsigned port = COM_BASE; + + while ((inb (port + 5) & 0x20) == 0) ; + outb (c, port); + if (c == 0x0a) { + while ((inb (port + 5) & 0x20) == 0) ; + outb (0x0d, port); + } +} + +static int ser_printk (const char *fmt, ...) +{ + va_list args; + int i; + long flags; + + spin_lock_irqsave (&serial_lock, flags); + va_start (args, fmt); + i = vsprintf (strbuf, fmt, args); + ser_puts (strbuf); + va_end (args); + spin_unlock_irqrestore (&serial_lock, flags); + + return i; +} + +#define TRACE(a) { ser_printk a;} + +#else +#define TRACE(A) +#endif + +#define TRACE1(a) + +static void callDone (Scsi_Cmnd * SCpnt) +{ + if (SCpnt->result) { + TRACE (("*** %.08lx %.02x <%d.%d.%d> = %x\n", + SCpnt->serial_number, SCpnt->cmnd[0], SCpnt->channel, + SCpnt->target, SCpnt->lun, SCpnt->result)); + } + SCpnt->scsi_done (SCpnt); +} + +/*------------------------------------------------------------------------- + * + * Local functions + * + *-------------------------------------------------------------------------*/ + +/*======================= + * Free a SCB structure + *======================= + */ +static void mega_freeSCB (mega_host_config * megaCfg, mega_scb * pScb) +{ + + mega_scb *pScbtmp; + + if ((pScb == NULL) || (pScb->idx >= 0xFE)) { + return; + } +#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,4,0) + switch (pScb->dma_type) { + case M_RD_DMA_TYPE_NONE: + break; + case M_RD_PTHRU_WITH_BULK_DATA: + pci_unmap_single (megaCfg->dev, pScb->dma_h_bulkdata, + pScb->pthru->dataxferlen, + pScb->dma_direction); + break; + case M_RD_EPTHRU_WITH_BULK_DATA: + pci_unmap_single (megaCfg->dev, pScb->dma_h_bulkdata, + pScb->epthru->dataxferlen, + pScb->dma_direction); + break; + case M_RD_PTHRU_WITH_SGLIST: + { + int count; + for (count = 0; count < pScb->sglist_count; count++) { + pci_unmap_single (megaCfg->dev, + pScb->dma_h_sglist[count], + pScb->sgList[count].length, + pScb->dma_direction); + + } + break; + } + case M_RD_BULK_DATA_ONLY: + pci_unmap_single (megaCfg->dev, + pScb->dma_h_bulkdata, + pScb->iDataSize, pScb->dma_direction); + + break; + case M_RD_SGLIST_ONLY: + pci_unmap_sg (megaCfg->dev, + pScb->SCpnt->request_buffer, + pScb->SCpnt->use_sg, pScb->dma_direction); + break; + default: + break; + } +#endif + + /* Unlink from pending queue */ + if (pScb == megaCfg->qPendingH) { + + if (megaCfg->qPendingH == megaCfg->qPendingT) + megaCfg->qPendingH = megaCfg->qPendingT = NULL; + else + megaCfg->qPendingH = megaCfg->qPendingH->next; + + megaCfg->qPcnt--; + + } else { + for (pScbtmp = megaCfg->qPendingH; pScbtmp; + pScbtmp = pScbtmp->next) { + + if (pScbtmp->next == pScb) { + + pScbtmp->next = pScb->next; + + if (pScb == megaCfg->qPendingT) { + megaCfg->qPendingT = pScbtmp; + } + + megaCfg->qPcnt--; + break; + } + } + } + + /* Link back into free list */ + pScb->state = SCB_FREE; + pScb->SCpnt = NULL; + + if (megaCfg->qFreeH == (mega_scb *) NULL) { + megaCfg->qFreeH = megaCfg->qFreeT = pScb; + } else { + megaCfg->qFreeT->next = pScb; + megaCfg->qFreeT = pScb; + } + + megaCfg->qFreeT->next = NULL; + megaCfg->qFcnt++; + +} + +/*=========================== + * Allocate a SCB structure + *=========================== + */ +static mega_scb *mega_allocateSCB (mega_host_config * megaCfg, Scsi_Cmnd * SCpnt) +{ + mega_scb *pScb; + + /* Unlink command from Free List */ + if ((pScb = megaCfg->qFreeH) != NULL) { + megaCfg->qFreeH = pScb->next; + megaCfg->qFcnt--; + + pScb->isrcount = jiffies; + pScb->next = NULL; + pScb->state = SCB_ACTIVE; + pScb->SCpnt = SCpnt; + +#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,4,0) + pScb->dma_type = M_RD_DMA_TYPE_NONE; +#endif + + return pScb; + } + + printk (KERN_WARNING "Megaraid: Could not allocate free SCB!!!\n"); + + return NULL; +} + +/* Run through the list of completed requests and finish it */ +static void mega_rundoneq (mega_host_config * megaCfg) +{ + Scsi_Cmnd *SCpnt; + + while ((SCpnt = megaCfg->qCompletedH) != NULL) { + megaCfg->qCompletedH = (Scsi_Cmnd *) SCpnt->host_scribble; + megaCfg->qCcnt--; + + SCpnt->host_scribble = (unsigned char *) NULL; /* XC : sep 14 */ + /* Callback */ + callDone (SCpnt); + } + + megaCfg->qCompletedH = megaCfg->qCompletedT = NULL; +} + +/* + * Runs through the list of pending requests + * Assumes that mega_lock spin_lock has been acquired. + */ +static int mega_runpendq (mega_host_config * megaCfg) +{ + mega_scb *pScb; + int rc; + + /* Issue any pending commands to the card */ + for (pScb = megaCfg->qPendingH; pScb; pScb = pScb->next) { + if (pScb->state == SCB_ACTIVE) { + if ((rc = + megaIssueCmd (megaCfg, pScb->mboxData, pScb, 1)) == -1) + return rc; + } + } + return 0; +} + +/* Add command to the list of completed requests */ + +static void mega_cmd_done (mega_host_config * megaCfg, mega_scb * pScb, int status) +{ + int islogical; + Scsi_Cmnd *SCpnt; + mega_passthru *pthru; + mega_ext_passthru *epthru; + mega_mailbox *mbox; + struct scatterlist *sgList; + u8 c; + + if (pScb == NULL) { + TRACE (("NULL pScb in mega_cmd_done!")); + printk(KERN_CRIT "NULL pScb in mega_cmd_done!"); + } + + SCpnt = pScb->SCpnt; + +#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,4,0) + pthru = pScb->pthru; + epthru = pScb->epthru; +#else + pthru = &pScb->pthru; + epthru = &pScb->epthru; +#endif + + mbox = (mega_mailbox *) & pScb->mboxData; + + if (SCpnt == NULL) { + TRACE (("NULL SCpnt in mega_cmd_done!")); + TRACE (("pScb->idx = ", pScb->idx)); + TRACE (("pScb->state = ", pScb->state)); + TRACE (("pScb->state = ", pScb->state)); + panic(KERN_ERR "megaraid:Problem...!\n"); + } + +#if 0 + islogical = ( (SCpnt->channel >= megaCfg->productInfo.SCSIChanPresent) && + (SCpnt->channel <= megaCfg->host->max_channel) ); +#endif +#if 0 + islogical = (SCpnt->channel == megaCfg->host->max_channel); +#endif + islogical = megaCfg->logdrv_chan[SCpnt->channel]; + +#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,4,0) + /* Special Case to handle PassThrough->XferAddrress > 4GB */ + switch (SCpnt->cmnd[0]) { + case INQUIRY: + case READ_CAPACITY: + memcpy (SCpnt->request_buffer, + pScb->bounce_buffer, SCpnt->request_bufflen); + break; + } +#endif + + mega_freeSCB (megaCfg, pScb); + + /* + * Do not return the presence of hard disk on the channel so, inquiry + * sent, and returned data==hard disk or removable hard disk and not + * logical, request should return failure! - PJ + */ +#if 0 + if (SCpnt->cmnd[0] == INQUIRY && ((((u_char *) SCpnt->request_buffer)[0] & 0x1F) == TYPE_DISK) && !islogical) { + status = 0xF0; + } +#endif + if (SCpnt->cmnd[0] == INQUIRY && !islogical) { + if ( SCpnt->use_sg ) { + sgList = (struct scatterlist *)SCpnt->request_buffer; + memcpy(&c, sgList[0].address, 0x1); + } else { + memcpy(&c, SCpnt->request_buffer, 0x1); + } +#if 0 + if( (c & 0x1F ) == TYPE_DISK ) { + status = 0xF0; + } +#endif + if(IS_RAID_CH(megaCfg, SCpnt->channel) && ((c & 0x1F) == TYPE_DISK)) { + status = 0xF0; + } + } + + + /* clear result; otherwise, success returns corrupt value */ + SCpnt->result = 0; + + if ( 0 && SCpnt->cmnd[0] & M_RD_IOCTL_CMD ) { /* i.e. ioctl cmd such as M_RD_IOCTL_CMD, M_RD_IOCTL_CMD_NEW of megamgr */ + switch (status) { + case 2: + case 0xF0: + case 0xF4: + SCpnt->result = (DID_BAD_TARGET << 16) | status; + break; + default: + SCpnt->result |= status; + } /*end of switch */ + } else { + /* Convert MegaRAID status to Linux error code */ + switch (status) { + case 0x00: /* SUCCESS , i.e. SCSI_STATUS_GOOD */ + SCpnt->result |= (DID_OK << 16); + break; + + case 0x02: /* ERROR_ABORTED, i.e. SCSI_STATUS_CHECK_CONDITION */ + + /*set sense_buffer and result fields */ + if (mbox->cmd == MEGA_MBOXCMD_PASSTHRU || mbox->cmd == + MEGA_MBOXCMD_PASSTHRU64 ) { + + memcpy (SCpnt->sense_buffer, pthru->reqsensearea, 14); + + SCpnt->result = (DRIVER_SENSE << 24) | (DID_OK << 16) | + (CHECK_CONDITION << 1); + + } else if (mbox->cmd == MEGA_MBOXCMD_EXTPASSTHRU) { + + memcpy( SCpnt->sense_buffer, epthru->reqsensearea, 14); + + SCpnt->result = (DRIVER_SENSE << 24) | (DID_OK << 16) | + (CHECK_CONDITION << 1); + + } else { + SCpnt->sense_buffer[0] = 0x70; + SCpnt->sense_buffer[2] = ABORTED_COMMAND; + SCpnt->result |= (CHECK_CONDITION << 1); + } + break; + + case 0x08: /* ERR_DEST_DRIVE_FAILED, i.e. SCSI_STATUS_BUSY */ + SCpnt->result |= (DID_BUS_BUSY << 16) | status; + break; + + default: + SCpnt->result |= (DID_BAD_TARGET << 16) | status; + break; + } + } + + /* Add Scsi_Command to end of completed queue */ + if (megaCfg->qCompletedH == NULL) { + megaCfg->qCompletedH = megaCfg->qCompletedT = SCpnt; + } else { + megaCfg->qCompletedT->host_scribble = (unsigned char *) SCpnt; + megaCfg->qCompletedT = SCpnt; + } + + megaCfg->qCompletedT->host_scribble = (unsigned char *) NULL; + megaCfg->qCcnt++; +} + +/*------------------------------------------------------------------- + * + * Build a SCB from a Scsi_Cmnd + * + * Returns a SCB pointer, or NULL + * If NULL is returned, the scsi_done function MUST have been called + * + *-------------------------------------------------------------------*/ + +static mega_scb *mega_build_cmd (mega_host_config * megaCfg, Scsi_Cmnd * SCpnt) +{ + mega_scb *pScb; + mega_mailbox *mbox; + mega_passthru *pthru; + mega_ext_passthru *epthru; + long seg; + char islogical; + int max_ldrv_num; + int channel = 0; + int target = 0; + int ldrv_num = 0; /* logical drive number */ + + if ((SCpnt->cmnd[0] == MEGADEVIOC)) + return megadev_doioctl (megaCfg, SCpnt); + + if ((SCpnt->cmnd[0] == M_RD_IOCTL_CMD) + || (SCpnt->cmnd[0] == M_RD_IOCTL_CMD_NEW)) +#if LINUX_VERSION_CODE < KERNEL_VERSION(2,3,0) + return mega_ioctl (megaCfg, SCpnt); /* Handle IOCTL command */ +#else + { + printk(KERN_WARNING "megaraid ioctl: older interface - " + "not supported.\n"); + return NULL; + } +#endif + +#if 0 + islogical = ( (SCpnt->channel >= megaCfg->productInfo.SCSIChanPresent) && + (SCpnt->channel <= megaCfg->host->max_channel) ); +#endif +#if 0 + islogical = (IS_RAID_CH(SCpnt->channel) && /* virtual ch is raid - AM */ + (SCpnt->channel == megaCfg->host->max_channel)); +#endif + + /* + * We know on what channels are our logical drives - mega_findCard() + */ + islogical = megaCfg->logdrv_chan[SCpnt->channel]; + + /* + * The theory: If physical drive is chosen for boot, all the physical + * devices are exported before the logical drives, otherwise physical + * devices are pushed after logical drives, in which case - Kernel sees + * the physical devices on virtual channel which is obviously converted + * to actual channel on the HBA. + */ + if( megaCfg->boot_pdrv_enabled ) { + if( islogical ) { + /* logical channel */ + channel = SCpnt->channel - megaCfg->productInfo.SCSIChanPresent; + } + else { + channel = SCpnt->channel; /* this is physical channel */ + target = SCpnt->target; + + /* + * boot from a physical disk, that disk needs to be exposed first + * IF both the channels are SCSI, then booting from the second + * channel is not allowed. + */ + if( target == 0 ) { + target = megaCfg->boot_pdrv_tgt; + } + else if( target == megaCfg->boot_pdrv_tgt ) { + target = 0; + } + } + } + else { + if( islogical ) { + channel = SCpnt->channel; /* this is the logical channel */ + } + else { + channel = SCpnt->channel - NVIRT_CHAN; /* physical channel */ + target = SCpnt->target; + } + } + + if ( ! megaCfg->support_ext_cdb ) { + if (!islogical && SCpnt->lun != 0) { + SCpnt->result = (DID_BAD_TARGET << 16); + callDone (SCpnt); + return NULL; + } + } + + if (!islogical && SCpnt->target == skip_id) { + SCpnt->result = (DID_BAD_TARGET << 16); + callDone (SCpnt); + return NULL; + } + + if (islogical) { + + /* have just LUN 0 for each target on virtual channels */ + if( SCpnt->lun != 0 ) { + SCpnt->result = (DID_BAD_TARGET << 16); + callDone (SCpnt); + return NULL; + } + + ldrv_num = mega_get_ldrv_num(megaCfg, SCpnt, channel); + + max_ldrv_num = (megaCfg->flag & BOARD_40LD) ? + FC_MAX_LOGICAL_DRIVES : MAX_LOGICAL_DRIVES; + + /* + * max_ldrv_num increases by 0x80 if some logical drive was deleted. + */ + if(megaCfg->read_ldidmap) { + max_ldrv_num += 0x80; + } + + if( ldrv_num > max_ldrv_num ) { + SCpnt->result = (DID_BAD_TARGET << 16); + callDone (SCpnt); + return NULL; + } + + } else { + if ( SCpnt->lun > 7) { + /* Do not support lun >7 for physically accessed devices */ + SCpnt->result = (DID_BAD_TARGET << 16); + callDone (SCpnt); + return NULL; + } + } + /*----------------------------------------------------- + * + * Logical drive commands + * + *-----------------------------------------------------*/ + if (islogical) { + switch (SCpnt->cmnd[0]) { + case TEST_UNIT_READY: + memset (SCpnt->request_buffer, 0, SCpnt->request_bufflen); + SCpnt->result = (DID_OK << 16); + callDone (SCpnt); + return NULL; + + case MODE_SENSE: + memset (SCpnt->request_buffer, 0, SCpnt->cmnd[4]); + SCpnt->result = (DID_OK << 16); + callDone (SCpnt); + return NULL; + + case READ_CAPACITY: + case INQUIRY: + if(!(megaCfg->flag & (1L << SCpnt->channel))) { + printk(KERN_NOTICE + "scsi%d: scanning virtual channel %d for logical drives.\n", + megaCfg->host->host_no, channel); + + megaCfg->flag |= (1L << SCpnt->channel); + } + + /* Allocate a SCB and initialize passthru */ + if ((pScb = mega_allocateSCB (megaCfg, SCpnt)) == NULL) { + SCpnt->result = (DID_ERROR << 16); + callDone (SCpnt); + return NULL; + } +#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,4,0) + pthru = pScb->pthru; +#else + pthru = &pScb->pthru; +#endif + + mbox = (mega_mailbox *) & pScb->mboxData; + memset (mbox, 0, sizeof (pScb->mboxData)); + memset (pthru, 0, sizeof (mega_passthru)); + pthru->timeout = 0; + pthru->ars = 1; + pthru->reqsenselen = 14; + pthru->islogical = 1; + pthru->logdrv = ldrv_num; + pthru->cdblen = SCpnt->cmd_len; + +#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,4,0) + /*Not sure about the direction */ + pScb->dma_direction = PCI_DMA_BIDIRECTIONAL; + pScb->dma_type = M_RD_PTHRU_WITH_BULK_DATA; + +#if 0 +/* Normal Code w/o the need for bounce buffer */ + pScb->dma_h_bulkdata + = pci_map_single (megaCfg->dev, + SCpnt->request_buffer, + SCpnt->request_bufflen, + pScb->dma_direction); + + pthru->dataxferaddr = pScb->dma_h_bulkdata; +#else +/* Special Code to use bounce buffer for READ_CAPA/INQ */ + pthru->dataxferaddr = pScb->dma_bounce_buffer; + pScb->dma_type = M_RD_DMA_TYPE_NONE; +#endif + +#else + pthru->dataxferaddr = + virt_to_bus (SCpnt->request_buffer); +#endif + + pthru->dataxferlen = SCpnt->request_bufflen; + memcpy (pthru->cdb, SCpnt->cmnd, SCpnt->cmd_len); + + /* Initialize mailbox area */ + mbox->cmd = MEGA_MBOXCMD_PASSTHRU; + +#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,4,0) + mbox->xferaddr = pScb->dma_passthruhandle64; + TRACE1 (("M_RD_PTHRU_WITH_BULK_DATA Enabled \n")); +#else + mbox->xferaddr = virt_to_bus (pthru); +#endif + return pScb; + + case READ_6: + case WRITE_6: + case READ_10: + case WRITE_10: + /* Allocate a SCB and initialize mailbox */ + if ((pScb = mega_allocateSCB (megaCfg, SCpnt)) == NULL) { + SCpnt->result = (DID_ERROR << 16); + callDone (SCpnt); + return NULL; + } + mbox = (mega_mailbox *) & pScb->mboxData; + + memset (mbox, 0, sizeof (pScb->mboxData)); + mbox->logdrv = ldrv_num; + + if (megaCfg->flag & BOARD_64BIT) { + mbox->cmd = (*SCpnt->cmnd == READ_6 + || *SCpnt->cmnd == + READ_10) ? MEGA_MBOXCMD_LREAD64 : + MEGA_MBOXCMD_LWRITE64; + } else { + mbox->cmd = (*SCpnt->cmnd == READ_6 + || *SCpnt->cmnd == + READ_10) ? MEGA_MBOXCMD_LREAD : + MEGA_MBOXCMD_LWRITE; + } + + /* 6-byte */ + if (*SCpnt->cmnd == READ_6 || *SCpnt->cmnd == WRITE_6) { + mbox->numsectors = (u32) SCpnt->cmnd[4]; + mbox->lba = + ((u32) SCpnt->cmnd[1] << 16) | + ((u32) SCpnt->cmnd[2] << 8) | + (u32) SCpnt->cmnd[3]; + mbox->lba &= 0x1FFFFF; + + if (*SCpnt->cmnd == READ_6) { + megaCfg->nReads[(int)ldrv_num]++; + megaCfg->nReadBlocks[(int)ldrv_num] += + mbox->numsectors; + } else { + megaCfg->nWrites[(int)ldrv_num]++; + megaCfg->nWriteBlocks[(int)ldrv_num] += + mbox->numsectors; + } + } + + /* 10-byte */ + if (*SCpnt->cmnd == READ_10 || *SCpnt->cmnd == WRITE_10) { + mbox->numsectors = + (u32) SCpnt->cmnd[8] | + ((u32) SCpnt->cmnd[7] << 8); + mbox->lba = + ((u32) SCpnt->cmnd[2] << 24) | + ((u32) SCpnt->cmnd[3] << 16) | + ((u32) SCpnt->cmnd[4] << 8) | + (u32) SCpnt->cmnd[5]; + + if (*SCpnt->cmnd == READ_10) { + megaCfg->nReads[(int)ldrv_num]++; + megaCfg->nReadBlocks[(int)ldrv_num] += + mbox->numsectors; + } else { + megaCfg->nWrites[(int)ldrv_num]++; + megaCfg->nWriteBlocks[(int)ldrv_num] += + mbox->numsectors; + } + } + + /* 12-byte */ + if (*SCpnt->cmnd == READ_12 || *SCpnt->cmnd == WRITE_12) { + mbox->lba = + ((u32) SCpnt->cmnd[2] << 24) | + ((u32) SCpnt->cmnd[3] << 16) | + ((u32) SCpnt->cmnd[4] << 8) | + (u32) SCpnt->cmnd[5]; + + mbox->numsectors = + ((u32) SCpnt->cmnd[6] << 24) | + ((u32) SCpnt->cmnd[7] << 16) | + ((u32) SCpnt->cmnd[8] << 8) | + (u32) SCpnt->cmnd[9]; + + if (*SCpnt->cmnd == READ_12) { + megaCfg->nReads[(int)ldrv_num]++; + megaCfg->nReadBlocks[(int)ldrv_num] += + mbox->numsectors; + } else { + megaCfg->nWrites[(int)ldrv_num]++; + megaCfg->nWriteBlocks[(int)ldrv_num] += + mbox->numsectors; + } + } + +#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,4,0) + if (*SCpnt->cmnd == READ_6 || *SCpnt->cmnd == READ_10 + || *SCpnt->cmnd == READ_12) { + pScb->dma_direction = PCI_DMA_FROMDEVICE; + } else { /*WRITE_6 or WRITE_10 */ + pScb->dma_direction = PCI_DMA_TODEVICE; + } +#endif + + /* Calculate Scatter-Gather info */ + mbox->numsgelements = mega_build_sglist (megaCfg, pScb, + (u32 *)&mbox->xferaddr, (u32 *)&seg); + +#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,4,0) + pScb->iDataSize = seg; + + if (mbox->numsgelements) { + pScb->dma_type = M_RD_SGLIST_ONLY; + TRACE1 (("M_RD_SGLIST_ONLY Enabled \n")); + } else { + pScb->dma_type = M_RD_BULK_DATA_ONLY; + TRACE1 (("M_RD_BULK_DATA_ONLY Enabled \n")); + } +#endif + + return pScb; + default: + SCpnt->result = (DID_BAD_TARGET << 16); + callDone (SCpnt); + return NULL; + } + } + /*----------------------------------------------------- + * + * Passthru drive commands + * + *-----------------------------------------------------*/ + else { + /* Allocate a SCB and initialize passthru */ + if ((pScb = mega_allocateSCB (megaCfg, SCpnt)) == NULL) { + SCpnt->result = (DID_ERROR << 16); + callDone (SCpnt); + return NULL; + } + + mbox = (mega_mailbox *) pScb->mboxData; + memset (mbox, 0, sizeof (pScb->mboxData)); + + if ( megaCfg->support_ext_cdb && SCpnt->cmd_len > 10 ) { + epthru = mega_prepare_extpassthru(megaCfg, pScb, SCpnt, channel, + target); + mbox->cmd = MEGA_MBOXCMD_EXTPASSTHRU; +#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,4,0) + mbox->xferaddr = pScb->dma_ext_passthruhandle64; + + if(epthru->numsgelements) { + pScb->dma_type = M_RD_PTHRU_WITH_SGLIST; + } else { + pScb->dma_type = M_RD_EPTHRU_WITH_BULK_DATA; + } +#else + mbox->xferaddr = virt_to_bus(epthru); +#endif + } + else { + pthru = mega_prepare_passthru(megaCfg, pScb, SCpnt, channel, + target); + + /* Initialize mailbox */ + mbox->cmd = MEGA_MBOXCMD_PASSTHRU; +#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,4,0) + mbox->xferaddr = pScb->dma_passthruhandle64; + + if (pthru->numsgelements) { + pScb->dma_type = M_RD_PTHRU_WITH_SGLIST; + } else { + pScb->dma_type = M_RD_PTHRU_WITH_BULK_DATA; + } +#else + mbox->xferaddr = virt_to_bus(pthru); +#endif + } + return pScb; + } + return NULL; +} + +static int +mega_get_ldrv_num(mega_host_config *this_hba, Scsi_Cmnd *sc, int channel) +{ + int tgt; + int ldrv_num; + + tgt = sc->target; + + if ( tgt > 7 ) tgt--; /* we do not get inquires for tgt 7 */ + + ldrv_num = (channel * 15) + tgt; /* 14 targets per channel */ + + /* + * If we have a logical drive with boot enabled, project it first + */ + if( this_hba->boot_ldrv_enabled ) { + if( ldrv_num == 0 ) { + ldrv_num = this_hba->boot_ldrv; + } + else { + if( ldrv_num <= this_hba->boot_ldrv ) { + ldrv_num--; + } + } + } + + /* + * If "delete logical drive" feature is enabled on this controller. + * Do only if at least one delete logical drive operation was done. + * + * Also, after logical drive deletion, instead of logical drive number, + * the value returned should be 0x80+logical drive id. + * + * These is valid only for IO commands. + */ + + if( this_hba->support_random_del && this_hba->read_ldidmap ) { + switch(sc->cmnd[0]) { + case READ_6: /* fall through */ + case WRITE_6: /* fall through */ + case READ_10: /* fall through */ + case WRITE_10: + ldrv_num += 0x80; + } + } + + return ldrv_num; +} + + +static mega_passthru * +mega_prepare_passthru(mega_host_config *megacfg, mega_scb *scb, Scsi_Cmnd *sc, + int channel, int target) +{ + mega_passthru *pthru; + +#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,4,0) + pthru = scb->pthru; +#else + pthru = &scb->pthru; +#endif + memset (pthru, 0, sizeof (mega_passthru)); + + /* set adapter timeout value to 10 min. for tape drive */ + /* 0=6sec/1=60sec/2=10min/3=3hrs */ + pthru->timeout = 2; + pthru->ars = 1; + pthru->reqsenselen = 14; + pthru->islogical = 0; + pthru->channel = (megacfg->flag & BOARD_40LD) ? 0 : channel; + pthru->target = (megacfg->flag & BOARD_40LD) ? + (channel << 4) | target : target; + pthru->cdblen = sc->cmd_len; + pthru->logdrv = sc->lun; + + memcpy (pthru->cdb, sc->cmnd, sc->cmd_len); + +#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,4,0) + /* Not sure about the direction */ + scb->dma_direction = PCI_DMA_BIDIRECTIONAL; + + /* Special Code for Handling READ_CAPA/ INQ using bounce buffers */ + switch (sc->cmnd[0]) { + case INQUIRY: + case READ_CAPACITY: + + if(!(megacfg->flag & (1L << sc->channel))) { + printk(KERN_NOTICE + "scsi%d: scanning physical channel %d for devices.\n", + megacfg->host->host_no, channel); + + megacfg->flag |= (1L << sc->channel); + } + + pthru->numsgelements = 0; + pthru->dataxferaddr = scb->dma_bounce_buffer; + pthru->dataxferlen = sc->request_bufflen; + break; + default: + pthru->numsgelements = + mega_build_sglist( + megacfg, scb, (u32 *)&pthru->dataxferaddr, + (u32 *)&pthru->dataxferlen + ); + break; + } +#else + pthru->numsgelements = + mega_build_sglist( + megacfg, scb, (u32 *)&pthru->dataxferaddr, + (u32 *)&pthru->dataxferlen + ); +#endif + return pthru; +} + +static mega_ext_passthru * +mega_prepare_extpassthru(mega_host_config *megacfg, mega_scb *scb, + Scsi_Cmnd *sc, int channel, int target) +{ + mega_ext_passthru *epthru; + +#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,4,0) + epthru = scb->epthru; +#else + epthru = &scb->epthru; +#endif + memset(epthru, 0, sizeof(mega_ext_passthru)); + + /* set adapter timeout value to 10 min. for tape drive */ + /* 0=6sec/1=60sec/2=10min/3=3hrs */ + epthru->timeout = 2; + epthru->ars = 1; + epthru->reqsenselen = 14; + epthru->islogical = 0; + epthru->channel = (megacfg->flag & BOARD_40LD) ? 0 : channel; + epthru->target = (megacfg->flag & BOARD_40LD) ? + (channel << 4) | target : target; + epthru->cdblen = sc->cmd_len; + epthru->logdrv = sc->lun; + + memcpy(epthru->cdb, sc->cmnd, sc->cmd_len); + +#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,4,0) + /* Not sure about the direction */ + scb->dma_direction = PCI_DMA_BIDIRECTIONAL; + + /* Special Code for Handling READ_CAPA/ INQ using bounce buffers */ + switch (sc->cmnd[0]) { + case INQUIRY: + case READ_CAPACITY: + if(!(megacfg->flag & (1L << sc->channel))) { + printk(KERN_NOTICE + "scsi%d: scanning physical channel %d for devices.\n", + megacfg->host->host_no, channel); + + megacfg->flag |= (1L << sc->channel); + } + + epthru->numsgelements = 0; + epthru->dataxferaddr = scb->dma_bounce_buffer; + epthru->dataxferlen = sc->request_bufflen; + break; + default: + epthru->numsgelements = + mega_build_sglist( + megacfg, scb, (u32 *)&epthru->dataxferaddr, + (u32 *)&epthru->dataxferlen + ); + break; + } +#else + epthru->numsgelements = + mega_build_sglist( + megacfg, scb, (u32 *)&epthru->dataxferaddr, + (u32 *)&epthru->dataxferlen + ); +#endif + return epthru; +} + +/* Handle Driver Level IOCTLs + * Return value of 0 indicates this function could not handle , so continue + * processing +*/ + +static int mega_driver_ioctl (mega_host_config * megaCfg, Scsi_Cmnd * SCpnt) +{ + unsigned char *data = (unsigned char *) SCpnt->request_buffer; + mega_driver_info driver_info; + + /* If this is not our command dont do anything */ + if (SCpnt->cmnd[0] != M_RD_DRIVER_IOCTL_INTERFACE) + return 0; + + switch (SCpnt->cmnd[1]) { + case GET_DRIVER_INFO: + if (SCpnt->request_bufflen < sizeof (driver_info)) { + SCpnt->result = DID_BAD_TARGET << 16; + callDone (SCpnt); + return 1; + } + + driver_info.size = sizeof (driver_info) - sizeof (int); + driver_info.version = MEGARAID_IOCTL_VERSION; + memcpy (data, &driver_info, sizeof (driver_info)); + break; + default: + SCpnt->result = DID_BAD_TARGET << 16; + } + + callDone (SCpnt); + return 1; +} + +static void inline set_mbox_xfer_addr (mega_host_config * megaCfg, mega_scb * pScb, + mega_ioctl_mbox * mbox, u32 direction) +{ + +#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,4,0) + switch (direction) { + case TO_DEVICE: + pScb->dma_direction = PCI_DMA_TODEVICE; + break; + case FROM_DEVICE: + pScb->dma_direction = PCI_DMA_FROMDEVICE; + break; + case FROMTO_DEVICE: + pScb->dma_direction = PCI_DMA_BIDIRECTIONAL; + break; + } + + pScb->dma_h_bulkdata + = pci_map_single (megaCfg->dev, + pScb->buff_ptr, + pScb->iDataSize, pScb->dma_direction); + mbox->xferaddr = pScb->dma_h_bulkdata; + pScb->dma_type = M_RD_BULK_DATA_ONLY; + TRACE1 (("M_RD_BULK_DATA_ONLY Enabled \n")); +#else + mbox->xferaddr = virt_to_bus (pScb->buff_ptr); +#endif +} + +#if LINUX_VERSION_CODE < KERNEL_VERSION(2,3,0) + +/*-------------------------------------------------------------------- + * build RAID commands for controller, passed down through ioctl() + *--------------------------------------------------------------------*/ +static mega_scb *mega_ioctl (mega_host_config * megaCfg, Scsi_Cmnd * SCpnt) +{ + mega_scb *pScb; + mega_ioctl_mbox *mbox; + mega_mailbox *mailbox; + mega_passthru *pthru; + u8 *mboxdata; + long seg, i = 0; + unsigned char *data = (unsigned char *) SCpnt->request_buffer; + + if ((pScb = mega_allocateSCB (megaCfg, SCpnt)) == NULL) { + SCpnt->result = (DID_ERROR << 16); + callDone (SCpnt); + return NULL; + } + pthru = &pScb->pthru; + + mboxdata = (u8 *) & pScb->mboxData; + mbox = (mega_ioctl_mbox *) & pScb->mboxData; + mailbox = (mega_mailbox *) & pScb->mboxData; + memset (mailbox, 0, sizeof (pScb->mboxData)); + + if (data[0] == 0x03) { /* passthrough command */ + unsigned char cdblen = data[2]; + memset (pthru, 0, sizeof (mega_passthru)); + pthru->islogical = (data[cdblen + 3] & 0x80) ? 1 : 0; + pthru->timeout = data[cdblen + 3] & 0x07; + pthru->reqsenselen = 14; + pthru->ars = (data[cdblen + 3] & 0x08) ? 1 : 0; + pthru->logdrv = data[cdblen + 4]; + pthru->channel = data[cdblen + 5]; + pthru->target = data[cdblen + 6]; + pthru->cdblen = cdblen; + memcpy (pthru->cdb, &data[3], cdblen); + + mailbox->cmd = MEGA_MBOXCMD_PASSTHRU; + + + pthru->numsgelements = mega_build_sglist (megaCfg, pScb, + (u32 *) & pthru-> + dataxferaddr, + (u32 *) & pthru-> + dataxferlen); + + mailbox->xferaddr = virt_to_bus (pthru); + + for (i = 0; i < (SCpnt->request_bufflen - cdblen - 7); i++) { + data[i] = data[i + cdblen + 7]; + } + return pScb; + } + /* else normal (nonpassthru) command */ + +#if LINUX_VERSION_CODE > KERNEL_VERSION(2,0,24) /*0x020024 */ + /* + *usage of the function copy from user is used in case of data more than + *4KB.This is used only with adapters which supports more than 8 logical + * drives.This feature is disabled on kernels earlier or same as 2.0.36 + * as the uaccess.h file is not available with those kernels. + */ + + if (SCpnt->cmnd[0] == M_RD_IOCTL_CMD_NEW) { + /* use external data area for large xfers */ + /* If cmnd[0] is set to M_RD_IOCTL_CMD_NEW then * + * cmnd[4..7] = external user buffer * + * cmnd[8..11] = length of buffer * + * */ + char *user_area = (char *)*((u32*)&SCpnt->cmnd[4]); + u32 xfer_size = *((u32 *) & SCpnt->cmnd[8]); + switch (data[0]) { + case FW_FIRE_WRITE: + case FW_FIRE_FLASH: + if ((ulong) user_area & (PAGE_SIZE - 1)) { + printk + ("megaraid:user address not aligned on 4K boundary.Error.\n"); + SCpnt->result = (DID_ERROR << 16); + callDone (SCpnt); + return NULL; + } + break; + default: + break; + } + + if (!(pScb->buff_ptr = kmalloc (xfer_size, GFP_KERNEL))) { + printk + ("megaraid: Insufficient mem for M_RD_IOCTL_CMD_NEW.\n"); + SCpnt->result = (DID_ERROR << 16); + callDone (SCpnt); + return NULL; + } + + copy_from_user (pScb->buff_ptr, user_area, xfer_size); + pScb->iDataSize = xfer_size; + + switch (data[0]) { + case DCMD_FC_CMD: + switch (data[1]) { + case DCMD_FC_READ_NVRAM_CONFIG: + case DCMD_GET_DISK_CONFIG: + { + if ((ulong) pScb-> + buff_ptr & (PAGE_SIZE - 1)) { + printk + ("megaraid:user address not sufficient Error.\n"); + SCpnt->result = + (DID_ERROR << 16); + callDone (SCpnt); + return NULL; + } + + /*building SG list */ + mega_build_kernel_sg (pScb->buff_ptr, + xfer_size, + pScb, mbox); + break; + } + default: + break; + } /*switch (data[1]) */ + break; + } + + } +#endif + + mbox->cmd = data[0]; + mbox->channel = data[1]; + mbox->param = data[2]; + mbox->pad[0] = data[3]; + mbox->logdrv = data[4]; + + if (SCpnt->cmnd[0] == M_RD_IOCTL_CMD_NEW) { + switch (data[0]) { + case FW_FIRE_WRITE: + mbox->cmd = FW_FIRE_WRITE; + mbox->channel = data[1]; /* Current Block Number */ + set_mbox_xfer_addr (megaCfg, pScb, mbox, TO_DEVICE); + mbox->numsgelements = 0; + break; + case FW_FIRE_FLASH: + mbox->cmd = FW_FIRE_FLASH; + mbox->channel = data[1] | 0x80; /* Origin */ + set_mbox_xfer_addr (megaCfg, pScb, mbox, TO_DEVICE); + mbox->numsgelements = 0; + break; + case DCMD_FC_CMD: + *(mboxdata + 0) = data[0]; /*mailbox byte 0: DCMD_FC_CMD */ + *(mboxdata + 2) = data[1]; /*sub command */ + switch (data[1]) { + case DCMD_FC_READ_NVRAM_CONFIG: + case DCMD_FC_READ_NVRAM_CONFIG_64: + /* number of elements in SG list */ + *(mboxdata + 3) = mbox->numsgelements; + if (megaCfg->flag & BOARD_64BIT) + *(mboxdata + 2) = + DCMD_FC_READ_NVRAM_CONFIG_64; + break; + case DCMD_WRITE_CONFIG: + case DCMD_WRITE_CONFIG_64: + if (megaCfg->flag & BOARD_64BIT) + *(mboxdata + 2) = DCMD_WRITE_CONFIG_64; + set_mbox_xfer_addr (megaCfg, pScb, mbox, + TO_DEVICE); + mbox->numsgelements = 0; + break; + case DCMD_GET_DISK_CONFIG: + case DCMD_GET_DISK_CONFIG_64: + if (megaCfg->flag & BOARD_64BIT) + *(mboxdata + 2) = + DCMD_GET_DISK_CONFIG_64; + *(mboxdata + 3) = data[2]; /*number of elements in SG list */ + /*nr of elements in SG list */ + *(mboxdata + 4) = mbox->numsgelements; + break; + case DCMD_DELETE_LOGDRV: + case DCMD_DELETE_DRIVEGROUP: + case NC_SUBOP_ENQUIRY3: + *(mboxdata + 3) = data[2]; + set_mbox_xfer_addr (megaCfg, pScb, mbox, + FROMTO_DEVICE); + mbox->numsgelements = 0; + break; + case DCMD_CHANGE_LDNO: + case DCMD_CHANGE_LOOPID: + *(mboxdata + 3) = data[2]; + *(mboxdata + 4) = data[3]; + set_mbox_xfer_addr (megaCfg, pScb, mbox, + TO_DEVICE); + mbox->numsgelements = 0; + break; + default: + set_mbox_xfer_addr (megaCfg, pScb, mbox, + FROMTO_DEVICE); + mbox->numsgelements = 0; + break; + } /*switch */ + break; + default: + set_mbox_xfer_addr (megaCfg, pScb, mbox, FROMTO_DEVICE); + mbox->numsgelements = 0; + break; + } + } else { + + mbox->numsgelements = mega_build_sglist (megaCfg, pScb, + (u32 *) & mbox-> + xferaddr, + (u32 *) & seg); + + /* Handling some of the fw special commands */ + switch (data[0]) { + case 6: /* START_DEV */ + mbox->xferaddr = *((u32 *) & data[i + 6]); + break; + default: + break; + } + + for (i = 0; i < (SCpnt->request_bufflen - 6); i++) { + data[i] = data[i + 6]; + } + } + + return (pScb); +} + + +static void mega_build_kernel_sg (char *barea, ulong xfersize, mega_scb * pScb, mega_ioctl_mbox * mbox) +{ + ulong i, buffer_area, len, end, end_page, x, idx = 0; + + buffer_area = (ulong) barea; + i = buffer_area; + end = buffer_area + xfersize; + end_page = (end) & ~(PAGE_SIZE - 1); + + do { + len = PAGE_SIZE - (i % PAGE_SIZE); + x = pScb->sgList[idx].address = + virt_to_bus ((volatile void *) i); + pScb->sgList[idx].length = len; + i += len; + idx++; + } while (i < end_page); + + if ((end - i) < 0) { + printk ("megaraid:Error in user address\n"); + } + + if (end - i) { + pScb->sgList[idx].address = virt_to_bus ((volatile void *) i); + pScb->sgList[idx].length = end - i; + idx++; + } + mbox->xferaddr = virt_to_bus (pScb->sgList); + mbox->numsgelements = idx; +} +#endif + + +#if DEBUG +static unsigned int cum_time = 0; +static unsigned int cum_time_cnt = 0; + +static void showMbox (mega_scb * pScb) +{ + mega_mailbox *mbox; + + if (pScb == NULL) + return; + + mbox = (mega_mailbox *) pScb->mboxData; + printk ("%u cmd:%x id:%x #scts:%x lba:%x addr:%x logdrv:%x #sg:%x\n", + pScb->SCpnt->pid, + mbox->cmd, mbox->cmdid, mbox->numsectors, + mbox->lba, mbox->xferaddr, mbox->logdrv, mbox->numsgelements); +} + +#endif + +/*-------------------------------------------------------------------- + * Interrupt service routine + *--------------------------------------------------------------------*/ +static void megaraid_isr (int irq, void *devp, struct pt_regs *regs) +{ + IO_LOCK_T; + mega_host_config * megaCfg; + u_char byte, idx, sIdx, tmpBox[MAILBOX_SIZE]; + u32 dword = 0; + mega_mailbox *mbox; + mega_scb *pScb; + u_char qCnt, qStatus; + u_char completed[MAX_FIRMWARE_STATUS]; + Scsi_Cmnd *SCpnt; + + megaCfg = (mega_host_config *) devp; + mbox = (mega_mailbox *) tmpBox; + + if (megaCfg->host->irq == irq) { + if (megaCfg->flag & IN_ISR) { + TRACE (("ISR called reentrantly!!\n")); + printk ("ISR called reentrantly!!\n"); + } + megaCfg->flag |= IN_ISR; + + if (mega_busyWaitMbox (megaCfg)) { + printk (KERN_WARNING "Error: mailbox busy in isr!\n"); + } + + /* Check if a valid interrupt is pending */ + if (megaCfg->flag & BOARD_QUARTZ) { + dword = RDOUTDOOR (megaCfg); + if (dword != 0x10001234) { + /* Spurious interrupt */ + megaCfg->flag &= ~IN_ISR; + return; + } + } else { + byte = READ_PORT (megaCfg->host->io_port, INTR_PORT); + if ((byte & VALID_INTR_BYTE) == 0) { + /* Spurious interrupt */ + megaCfg->flag &= ~IN_ISR; + return; + } + WRITE_PORT (megaCfg->host->io_port, INTR_PORT, byte); + } + + for (idx = 0; idx < MAX_FIRMWARE_STATUS; idx++) + completed[idx] = 0; + + IO_LOCK; + + megaCfg->nInterrupts++; + qCnt = 0xff; + while ((qCnt = megaCfg->mbox->numstatus) == 0xFF) ; + + qStatus = 0xff; + while ((qStatus = megaCfg->mbox->status) == 0xFF) ; + + /* Get list of completed requests */ + for (idx = 0; idx < qCnt; idx++) { + while ((sIdx = megaCfg->mbox->completed[idx]) == 0xFF) { + printk ("p"); + } + completed[idx] = sIdx; + sIdx = 0xFF; + } + + if (megaCfg->flag & BOARD_QUARTZ) { + WROUTDOOR (megaCfg, dword); + /* Acknowledge interrupt */ +#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,4,0) + /* In this case mbox contains physical address */ +#if 0 + WRINDOOR (megaCfg, megaCfg->adjdmahandle64 | 0x2); +#else + WRINDOOR (megaCfg, 0x2); +#endif + +#else + +#if 0 + WRINDOOR (megaCfg, virt_to_bus (megaCfg->mbox) | 0x2); +#else + WRINDOOR (megaCfg, 0x2); +#endif + +#endif + +#if 0 + while (RDINDOOR (megaCfg) & 0x02) ; +#endif + } else { + CLEAR_INTR (megaCfg->host->io_port); + } + +#if DEBUG + if (qCnt >= MAX_FIRMWARE_STATUS) { + printk ("megaraid_isr: cmplt=%d ", qCnt); + } +#endif + + for (idx = 0; idx < qCnt; idx++) { + sIdx = completed[idx]; + if ((sIdx > 0) && (sIdx <= MAX_COMMANDS)) { + pScb = &megaCfg->scbList[sIdx - 1]; + + /* ASSERT(pScb->state == SCB_ISSUED); */ + +#if DEBUG + if (((jiffies) - pScb->isrcount) > maxCmdTime) { + maxCmdTime = (jiffies) - pScb->isrcount; + printk + ("megaraid_isr : cmd time = %u\n", + maxCmdTime); + } +#endif + /* + * Assuming that the scsi command, for which + * an abort request was received earlier, has + * completed. + */ + if (pScb->state == SCB_ABORTED) { + SCpnt = pScb->SCpnt; + } + if (pScb->state == SCB_RESET) { + SCpnt = pScb->SCpnt; + mega_freeSCB (megaCfg, pScb); + SCpnt->result = (DID_RESET << 16); + if (megaCfg->qCompletedH == NULL) { + megaCfg->qCompletedH = + megaCfg->qCompletedT = + SCpnt; + } else { + megaCfg->qCompletedT-> + host_scribble = + (unsigned char *) SCpnt; + megaCfg->qCompletedT = SCpnt; + } + megaCfg->qCompletedT->host_scribble = + (unsigned char *) NULL; + megaCfg->qCcnt++; + continue; + } + + /* We don't want the ISR routine to touch M_RD_IOCTL_CMD_NEW commands, so + * don't mark them as complete, instead we pop their semaphore so + * that the queue routine can finish them off + */ + if (pScb->SCpnt->cmnd[0] == M_RD_IOCTL_CMD_NEW) { + /* save the status byte for the queue routine to use */ + pScb->SCpnt->result = qStatus; +#if XENO_KILLED + up (&pScb->ioctl_sem); +#endif + } else { + /* Mark command as completed */ + mega_cmd_done (megaCfg, pScb, qStatus); + } + } else { + printk + ("megaraid: wrong cmd id completed from firmware:id=%x\n", + sIdx); + } + } + + mega_rundoneq (megaCfg); + + megaCfg->flag &= ~IN_ISR; + /* Loop through any pending requests */ + mega_runpendq (megaCfg); + IO_UNLOCK; + + } + +} + +/*==================================================*/ +/* Wait until the controller's mailbox is available */ +/*==================================================*/ + +static int mega_busyWaitMbox (mega_host_config * megaCfg) +{ + mega_mailbox *mbox = (mega_mailbox *) megaCfg->mbox; + long counter; + + for (counter = 0; counter < 10000; counter++) { + if (!mbox->busy) { + return 0; + } + udelay (100); + barrier (); + } + return -1; /* give up after 1 second */ +} + +/*===================================================== + * Post a command to the card + * + * Arguments: + * mega_host_config *megaCfg - Controller structure + * u_char *mboxData - Mailbox area, 16 bytes + * mega_scb *pScb - SCB posting (or NULL if N/A) + * int intr - if 1, interrupt, 0 is blocking + * Return Value: (added on 7/26 for 40ld/64bit) + * -1: the command was not actually issued out + * other cases: + * intr==0, return ScsiStatus, i.e. mbox->status + * intr==1, return 0 + *===================================================== + */ +static int megaIssueCmd (mega_host_config * megaCfg, u_char * mboxData, + mega_scb * pScb, int intr) +{ + volatile mega_mailbox *mbox = (mega_mailbox *) megaCfg->mbox; + +#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,4,0) + volatile mega_mailbox64 *mbox64 = (mega_mailbox64 *) megaCfg->mbox64; +#endif + + u_char byte; + +#ifdef __LP64__ + u64 phys_mbox; +#else + u32 phys_mbox; +#endif + u8 retval = -1; + + mboxData[0x1] = (pScb ? pScb->idx + 1 : 0xFE); /* Set cmdid */ + mboxData[0xF] = 1; /* Set busy */ + +#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,4,0) + /* In this case mbox contains physical address */ + phys_mbox = megaCfg->adjdmahandle64; +#else + phys_mbox = virt_to_bus (megaCfg->mbox); +#endif + +#if DEBUG + ShowMbox (pScb); +#endif + + /* Wait until mailbox is free */ + if (mega_busyWaitMbox (megaCfg)) { + printk ("Blocked mailbox......!!\n"); + udelay (1000); + +#if DEBUG + showMbox (pLastScb); +#endif + + /* Abort command */ + if (pScb == NULL) { + TRACE (("NULL pScb in megaIssue\n")); + printk ("NULL pScb in megaIssue\n"); + } + mega_cmd_done (megaCfg, pScb, 0x08); + return -1; + } + + pLastScb = pScb; + + /* Copy mailbox data into host structure */ + megaCfg->mbox64->xferSegment_lo = 0; + megaCfg->mbox64->xferSegment_hi = 0; + + memcpy ((char *) mbox, mboxData, 16); + +#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,4,0) + switch (mboxData[0]) { + case MEGA_MBOXCMD_LREAD64: + case MEGA_MBOXCMD_LWRITE64: + mbox64->xferSegment_lo = mbox->xferaddr; + mbox64->xferSegment_hi = 0; + mbox->xferaddr = 0xFFFFFFFF; + break; + } +#endif + + /* Kick IO */ + if (intr) { + /* Issue interrupt (non-blocking) command */ + if (megaCfg->flag & BOARD_QUARTZ) { + mbox->mraid_poll = 0; + mbox->mraid_ack = 0; + + WRINDOOR (megaCfg, phys_mbox | 0x1); + } else { + ENABLE_INTR (megaCfg->host->io_port); + ISSUE_COMMAND (megaCfg->host->io_port); + } + pScb->state = SCB_ISSUED; + + retval = 0; + } else { /* Issue non-ISR (blocking) command */ + disable_irq (megaCfg->host->irq); + if (megaCfg->flag & BOARD_QUARTZ) { + mbox->mraid_poll = 0; + mbox->mraid_ack = 0; + mbox->numstatus = 0xFF; + mbox->status = 0xFF; + WRINDOOR (megaCfg, phys_mbox | 0x1); + + while (mbox->numstatus == 0xFF) ; + while (mbox->status == 0xFF) ; + while (mbox->mraid_poll != 0x77) ; + mbox->mraid_poll = 0; + mbox->mraid_ack = 0x77; + + /* while ((cmdDone = RDOUTDOOR (megaCfg)) != 0x10001234); + WROUTDOOR (megaCfg, cmdDone); */ + + if (pScb) { + mega_cmd_done (megaCfg, pScb, mbox->status); + } + + WRINDOOR (megaCfg, phys_mbox | 0x2); + while (RDINDOOR (megaCfg) & 0x2) ; + + } else { + DISABLE_INTR (megaCfg->host->io_port); + ISSUE_COMMAND (megaCfg->host->io_port); + + while (! + ((byte = + READ_PORT (megaCfg->host->io_port, + INTR_PORT)) & INTR_VALID)) ; + WRITE_PORT (megaCfg->host->io_port, INTR_PORT, byte); + + ENABLE_INTR (megaCfg->host->io_port); + CLEAR_INTR (megaCfg->host->io_port); + + if (pScb) { + mega_cmd_done (megaCfg, pScb, mbox->status); + } else { + TRACE (("Error: NULL pScb!\n")); + } + } + enable_irq (megaCfg->host->irq); + retval = mbox->status; + } +#if DEBUG + while (mega_busyWaitMbox (megaCfg)) { + printk(KERN_ERR "Blocked mailbox on exit......!\n"); + udelay (1000); + } +#endif + + return retval; +} + +/*------------------------------------------------------------------- + * Copies data to SGLIST + *-------------------------------------------------------------------*/ +/* Note: + For 64 bit cards, we need a minimum of one SG element for read/write +*/ + +static int +mega_build_sglist (mega_host_config * megaCfg, mega_scb * scb, + u32 * buffer, u32 * length) +{ + struct scatterlist *sgList; + int idx; + +#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,4,0) + int sgcnt; +#endif + + mega_mailbox *mbox = NULL; + + mbox = (mega_mailbox *) scb->mboxData; + /* Scatter-gather not used */ + if (scb->SCpnt->use_sg == 0) { + +#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,4,0) + scb->dma_h_bulkdata = pci_map_single (megaCfg->dev, + scb->SCpnt->request_buffer, + scb->SCpnt->request_bufflen, + scb->dma_direction); + /* We need to handle special commands like READ64, WRITE64 + as they need a minimum of 1 SG irrespective of actually SG + */ + if ((megaCfg->flag & BOARD_64BIT) && + ((mbox->cmd == MEGA_MBOXCMD_LREAD64) || + (mbox->cmd == MEGA_MBOXCMD_LWRITE64))) { + scb->sg64List[0].address = scb->dma_h_bulkdata; + scb->sg64List[0].length = scb->SCpnt->request_bufflen; + *buffer = scb->dma_sghandle64; + *length = (u32)scb->SCpnt->request_bufflen; + scb->sglist_count = 1; + return 1; + } else { + *buffer = scb->dma_h_bulkdata; + *length = (u32) scb->SCpnt->request_bufflen; + } +#else + *buffer = virt_to_bus (scb->SCpnt->request_buffer); + *length = (u32) scb->SCpnt->request_bufflen; +#endif + return 0; + } + + sgList = (struct scatterlist *) scb->SCpnt->request_buffer; +#if 0 + if (scb->SCpnt->use_sg == 1) { + +#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,4,0) + scb->dma_h_bulkdata = pci_map_single (megaCfg->dev, + sgList[0].address, + sgList[0].length, scb->dma_direction); + + if ((megaCfg->flag & BOARD_64BIT) && + ((mbox->cmd == MEGA_MBOXCMD_LREAD64) || + (mbox->cmd == MEGA_MBOXCMD_LWRITE64))) { + scb->sg64List[0].address = scb->dma_h_bulkdata; + scb->sg64List[0].length = scb->SCpnt->request_bufflen; + *buffer = scb->dma_sghandle64; + *length = 0; + scb->sglist_count = 1; + return 1; + } else { + *buffer = scb->dma_h_bulkdata; + *length = (u32) sgList[0].length; + } +#else + *buffer = virt_to_bus (sgList[0].address); + *length = (u32) sgList[0].length; +#endif + + return 0; + } +#endif + /* Copy Scatter-Gather list info into controller structure */ +#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,4,0) + sgcnt = pci_map_sg (megaCfg->dev, + sgList, scb->SCpnt->use_sg, scb->dma_direction); + + /* Determine the validity of the new count */ + if (sgcnt == 0) + printk ("pci_map_sg returned zero!!! "); + + for (idx = 0; idx < sgcnt; idx++, sgList++) { + + if ((megaCfg->flag & BOARD_64BIT) && + ((mbox->cmd == MEGA_MBOXCMD_LREAD64) || + (mbox->cmd == MEGA_MBOXCMD_LWRITE64))) { + scb->sg64List[idx].address = sg_dma_address (sgList); + scb->sg64List[idx].length = sg_dma_len (sgList); + } else { + scb->sgList[idx].address = sg_dma_address (sgList); + scb->sgList[idx].length = sg_dma_len (sgList); + } + + } + +#else + for (idx = 0; idx < scb->SCpnt->use_sg; idx++) { + scb->sgList[idx].address = virt_to_bus (sgList[idx].address); + scb->sgList[idx].length = (u32) sgList[idx].length; + } +#endif + + /* Reset pointer and length fields */ +#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,4,0) + *buffer = scb->dma_sghandle64; + scb->sglist_count = scb->SCpnt->use_sg; +#else + *buffer = virt_to_bus (scb->sgList); +#endif + +#if 0 + *length = 0; +#endif + /* + * For passthru command, dataxferlen must be set, even for commands with a + * sg list + */ + *length = (u32)scb->SCpnt->request_bufflen; + +#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,4,0) + /* Return count of SG requests */ + return sgcnt; +#else + /* Return count of SG requests */ + return scb->SCpnt->use_sg; +#endif +} + +/*-------------------------------------------------------------------- + * Initializes the address of the controller's mailbox register + * The mailbox register is used to issue commands to the card. + * Format of the mailbox area: + * 00 01 command + * 01 01 command id + * 02 02 # of sectors + * 04 04 logical bus address + * 08 04 physical buffer address + * 0C 01 logical drive # + * 0D 01 length of scatter/gather list + * 0E 01 reserved + * 0F 01 mailbox busy + * 10 01 numstatus byte + * 11 01 status byte + *--------------------------------------------------------------------*/ +static int +mega_register_mailbox (mega_host_config * megaCfg, u32 paddr) +{ + /* align on 16-byte boundary */ +#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,4,0) + megaCfg->mbox = &megaCfg->mailbox64ptr->mailbox; +#else + megaCfg->mbox = &megaCfg->mailbox64.mailbox; +#endif + +#ifdef __LP64__ + megaCfg->mbox = (mega_mailbox *) ((((u64) megaCfg->mbox) + 16) & ((u64) (-1) ^ 0x0F)); + megaCfg->adjdmahandle64 = (megaCfg->dma_handle64 + 16) & ((u64) (-1) ^ 0x0F); + megaCfg->mbox64 = (mega_mailbox64 *) ((u_char *) megaCfg->mbox - sizeof (u64)); + paddr = (paddr + 4 + 16) & ((u64) (-1) ^ 0x0F); +#else + megaCfg->mbox + = (mega_mailbox *) ((((u32) megaCfg->mbox) + 16) & 0xFFFFFFF0); + +#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,4,0) + megaCfg->adjdmahandle64 = ((megaCfg->dma_handle64 + 16) & 0xFFFFFFF0); +#endif + + megaCfg->mbox64 = (mega_mailbox64 *) ((u_char *) megaCfg->mbox - 8); + paddr = (paddr + 4 + 16) & 0xFFFFFFF0; +#endif + + /* Register mailbox area with the firmware */ + if (!(megaCfg->flag & BOARD_QUARTZ)) { + WRITE_PORT (megaCfg->host->io_port, MBOX_PORT0, paddr & 0xFF); + WRITE_PORT (megaCfg->host->io_port, MBOX_PORT1, + (paddr >> 8) & 0xFF); + WRITE_PORT (megaCfg->host->io_port, MBOX_PORT2, + (paddr >> 16) & 0xFF); + WRITE_PORT (megaCfg->host->io_port, MBOX_PORT3, + (paddr >> 24) & 0xFF); + WRITE_PORT (megaCfg->host->io_port, ENABLE_MBOX_REGION, + ENABLE_MBOX_BYTE); + + CLEAR_INTR (megaCfg->host->io_port); + ENABLE_INTR (megaCfg->host->io_port); + } + return 0; +} + +/*--------------------------------------------------------------------------- + * mega_Convert8ldTo40ld() -- takes all info in AdapterInquiry structure and + * puts it into ProductInfo and Enquiry3 structures for later use + *---------------------------------------------------------------------------*/ +static void mega_Convert8ldTo40ld (mega_RAIDINQ * inquiry, + mega_Enquiry3 * enquiry3, + megaRaidProductInfo * productInfo) +{ + int i; + + productInfo->MaxConcCmds = inquiry->AdpInfo.MaxConcCmds; + enquiry3->rbldRate = inquiry->AdpInfo.RbldRate; + productInfo->SCSIChanPresent = inquiry->AdpInfo.ChanPresent; + + for (i = 0; i < 4; i++) { + productInfo->FwVer[i] = inquiry->AdpInfo.FwVer[i]; + productInfo->BiosVer[i] = inquiry->AdpInfo.BiosVer[i]; + } + enquiry3->cacheFlushInterval = inquiry->AdpInfo.CacheFlushInterval; + productInfo->DramSize = inquiry->AdpInfo.DramSize; + + enquiry3->numLDrv = inquiry->LogdrvInfo.NumLDrv; + + for (i = 0; i < MAX_LOGICAL_DRIVES; i++) { + enquiry3->lDrvSize[i] = inquiry->LogdrvInfo.LDrvSize[i]; + enquiry3->lDrvProp[i] = inquiry->LogdrvInfo.LDrvProp[i]; + enquiry3->lDrvState[i] + = inquiry->LogdrvInfo.LDrvState[i]; + } + + for (i = 0; i < (MAX_PHYSICAL_DRIVES); i++) { + enquiry3->pDrvState[i] + = inquiry->PhysdrvInfo.PDrvState[i]; + } +} + +/*------------------------------------------------------------------- + * Issue an adapter info query to the controller + *-------------------------------------------------------------------*/ +static int mega_i_query_adapter (mega_host_config * megaCfg) +{ + mega_Enquiry3 *enquiry3Pnt; + mega_mailbox *mbox; + u_char mboxData[16]; + +#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,4,0) + dma_addr_t raid_inq_dma_handle = 0, prod_info_dma_handle = 0, enquiry3_dma_handle = 0; +#endif + u8 retval; + + /* Initialize adapter inquiry mailbox */ + + mbox = (mega_mailbox *) mboxData; + + memset ((void *) megaCfg->mega_buffer, 0, + sizeof (megaCfg->mega_buffer)); + memset (mbox, 0, 16); + +/* + * Try to issue Enquiry3 command + * if not succeeded, then issue MEGA_MBOXCMD_ADAPTERINQ command and + * update enquiry3 structure + */ +#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,4,0) + enquiry3_dma_handle = pci_map_single (megaCfg->dev, + (void *) megaCfg->mega_buffer, + (2 * 1024L), PCI_DMA_FROMDEVICE); + + mbox->xferaddr = enquiry3_dma_handle; +#else + /*Taken care */ + mbox->xferaddr = virt_to_bus ((void *) megaCfg->mega_buffer); +#endif + + /* Initialize mailbox databuffer addr */ + enquiry3Pnt = (mega_Enquiry3 *) megaCfg->mega_buffer; + /* point mega_Enguiry3 to the data buf */ + + mboxData[0] = FC_NEW_CONFIG; /* i.e. mbox->cmd=0xA1 */ + mboxData[2] = NC_SUBOP_ENQUIRY3; /* i.e. 0x0F */ + mboxData[3] = ENQ3_GET_SOLICITED_FULL; /* i.e. 0x02 */ + + /* Issue a blocking command to the card */ + if ((retval = megaIssueCmd (megaCfg, mboxData, NULL, 0)) != 0) { /* the adapter does not support 40ld */ + mega_RAIDINQ adapterInquiryData; + mega_RAIDINQ *adapterInquiryPnt = &adapterInquiryData; + +#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,4,0) + raid_inq_dma_handle = pci_map_single (megaCfg->dev, + (void *) adapterInquiryPnt, + sizeof (mega_RAIDINQ), + PCI_DMA_FROMDEVICE); + mbox->xferaddr = raid_inq_dma_handle; +#else + /*taken care */ + mbox->xferaddr = virt_to_bus ((void *) adapterInquiryPnt); +#endif + + mbox->cmd = MEGA_MBOXCMD_ADAPTERINQ; /*issue old 0x05 command to adapter */ + /* Issue a blocking command to the card */ ; + retval = megaIssueCmd (megaCfg, mboxData, NULL, 0); + + pci_unmap_single (megaCfg->dev, + raid_inq_dma_handle, + sizeof (mega_RAIDINQ), PCI_DMA_FROMDEVICE); + + /*update Enquiry3 and ProductInfo structures with mega_RAIDINQ structure*/ + mega_Convert8ldTo40ld (adapterInquiryPnt, + enquiry3Pnt, + (megaRaidProductInfo *) & megaCfg-> + productInfo); + + } else { /* adapter supports 40ld */ + megaCfg->flag |= BOARD_40LD; + + pci_unmap_single (megaCfg->dev, + enquiry3_dma_handle, + (2 * 1024L), PCI_DMA_FROMDEVICE); +#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,4,0) +/*get productInfo, which is static information and will be unchanged*/ + prod_info_dma_handle + = pci_map_single (megaCfg->dev, + (void *) &megaCfg->productInfo, + sizeof (megaRaidProductInfo), + PCI_DMA_FROMDEVICE); + mbox->xferaddr = prod_info_dma_handle; +#else + /*taken care */ + mbox->xferaddr = virt_to_bus ((void *) &megaCfg->productInfo); +#endif + + mboxData[0] = FC_NEW_CONFIG; /* i.e. mbox->cmd=0xA1 */ + mboxData[2] = NC_SUBOP_PRODUCT_INFO; /* i.e. 0x0E */ + + if ((retval = megaIssueCmd (megaCfg, mboxData, NULL, 0)) != 0) + printk ("megaraid: Product_info cmd failed with error: %d\n", + retval); + + pci_unmap_single (megaCfg->dev, + prod_info_dma_handle, + sizeof (megaRaidProductInfo), + PCI_DMA_FROMDEVICE); + } + + /* + * kernel scans the channels from 0 to <= max_channel + */ + megaCfg->host->max_channel = + megaCfg->productInfo.SCSIChanPresent + NVIRT_CHAN -1; + + megaCfg->host->max_id = 16; /* max targets per channel */ + + megaCfg->host->max_lun = 7; /* Upto 7 luns for non disk devices */ + + megaCfg->host->cmd_per_lun = MAX_CMD_PER_LUN; + + megaCfg->numldrv = enquiry3Pnt->numLDrv; + megaCfg->max_cmds = megaCfg->productInfo.MaxConcCmds; + if (megaCfg->max_cmds > MAX_COMMANDS) + megaCfg->max_cmds = MAX_COMMANDS - 1; + + megaCfg->host->can_queue = megaCfg->max_cmds - 1; + + /* use HP firmware and bios version encoding */ + if (megaCfg->productInfo.subSystemVendorID == HP_SUBSYS_ID) { + sprintf (megaCfg->fwVer, "%c%d%d.%d%d", + megaCfg->productInfo.FwVer[2], + megaCfg->productInfo.FwVer[1] >> 8, + megaCfg->productInfo.FwVer[1] & 0x0f, + megaCfg->productInfo.FwVer[0] >> 8, + megaCfg->productInfo.FwVer[0] & 0x0f); + sprintf (megaCfg->biosVer, "%c%d%d.%d%d", + megaCfg->productInfo.BiosVer[2], + megaCfg->productInfo.BiosVer[1] >> 8, + megaCfg->productInfo.BiosVer[1] & 0x0f, + megaCfg->productInfo.BiosVer[0] >> 8, + megaCfg->productInfo.BiosVer[0] & 0x0f); + } else { + memcpy (megaCfg->fwVer, (char *) megaCfg->productInfo.FwVer, 4); + megaCfg->fwVer[4] = 0; + + memcpy (megaCfg->biosVer, (char *) megaCfg->productInfo.BiosVer, 4); + megaCfg->biosVer[4] = 0; + } + megaCfg->support_ext_cdb = mega_support_ext_cdb(megaCfg); + + printk (KERN_NOTICE "megaraid: [%s:%s] detected %d logical drives" M_RD_CRLFSTR, + megaCfg->fwVer, megaCfg->biosVer, megaCfg->numldrv); + + if ( megaCfg->support_ext_cdb ) { + printk(KERN_NOTICE "megaraid: supports extended CDBs.\n"); + } + + /* + * I hope that I can unmap here, reason DMA transaction is not required any more + * after this + */ + + return 0; +} + +/*------------------------------------------------------------------------- + * + * Driver interface functions + * + *-------------------------------------------------------------------------*/ + +/*---------------------------------------------------------- + * Returns data to be displayed in /proc/scsi/megaraid/X + *----------------------------------------------------------*/ +#if XENO_KILLED +int megaraid_proc_info (char *buffer, char **start, off_t offset, + int length, int host_no, int inout) +{ + *start = buffer; + return 0; +} +#endif + +static int mega_findCard (Scsi_Host_Template * pHostTmpl, + u16 pciVendor, u16 pciDev, long flag) +{ + mega_host_config *megaCfg = NULL; + struct Scsi_Host *host = NULL; + u_char pciBus, pciDevFun, megaIrq; + + u16 magic; +#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,4,0) + u32 magic64; +#endif + + int i, j; + +#ifdef __LP64__ + u64 megaBase; +#else + u32 megaBase; +#endif + + u16 pciIdx = 0; + u16 numFound = 0; + u16 subsysid, subsysvid; +#if 0 + u8 mega_ch_class; +#endif + +#if LINUX_VERSION_CODE < KERNEL_VERSION(2,1,0) /* 0x20100 */ + while (!pcibios_find_device + (pciVendor, pciDev, pciIdx, &pciBus, &pciDevFun)) { +#else + +#if LINUX_VERSION_CODE > KERNEL_VERSION(2,3,0) /*0x20300 */ + struct pci_dev *pdev = NULL; +#else + struct pci_dev *pdev = pci_devices; +#endif + + while ((pdev = pci_find_device (pciVendor, pciDev, pdev))) { + if(pci_enable_device (pdev)) + continue; + pciBus = pdev->bus->number; + pciDevFun = pdev->devfn; +#endif + if ((flag & BOARD_QUARTZ) && (skip_id == -1)) { + if( (pciVendor == PCI_VENDOR_ID_PERC4_DI_YSTONE && + pciDev == PCI_DEVICE_ID_PERC4_DI_YSTONE) || + (pciVendor == PCI_VENDOR_ID_PERC4_QC_VERDE && + pciDev == PCI_DEVICE_ID_PERC4_QC_VERDE) ) { + + flag |= BOARD_64BIT; + } + else { + pcibios_read_config_word (pciBus, pciDevFun, + PCI_CONF_AMISIG, &magic); + if ((magic != AMI_SIGNATURE) + && (magic != AMI_SIGNATURE_471)) { + pciIdx++; + continue; /* not an AMI board */ + } +#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,4,0) + pcibios_read_config_dword (pciBus, pciDevFun, + PCI_CONF_AMISIG64, &magic64); + + if (magic64 == AMI_64BIT_SIGNATURE) + flag |= BOARD_64BIT; +#endif + } + } + + /* Hmmm...Should we not make this more modularized so that in future we dont add + for each firmware */ + + if (flag & BOARD_QUARTZ) { + /* Check to see if this is a Dell PERC RAID controller model 466 */ +#if LINUX_VERSION_CODE < KERNEL_VERSION(2,1,0) /* 0x20100 */ + pcibios_read_config_word (pciBus, pciDevFun, + PCI_SUBSYSTEM_VENDOR_ID, + &subsysvid); + pcibios_read_config_word (pciBus, pciDevFun, + PCI_SUBSYSTEM_ID, &subsysid); +#else + pci_read_config_word (pdev, + PCI_SUBSYSTEM_VENDOR_ID, + &subsysvid); + pci_read_config_word (pdev, + PCI_SUBSYSTEM_ID, &subsysid); +#endif + + /* + * If we do not find the valid subsys vendor id, refuse to load + * the driver. This is part of PCI200X compliance + */ + if( (subsysvid != AMI_SUBSYS_ID) && + (subsysvid != DELL_SUBSYS_ID) && + (subsysvid != LSI_SUBSYS_ID) && + (subsysvid != HP_SUBSYS_ID) ) continue; + + } + + printk (KERN_NOTICE + "megaraid: found 0x%4.04x:0x%4.04x:idx %d:bus %d:slot %d:func %d\n", + pciVendor, pciDev, pciIdx, pciBus, PCI_SLOT (pciDevFun), + PCI_FUNC (pciDevFun)); + /* Read the base port and IRQ from PCI */ +#if LINUX_VERSION_CODE < KERNEL_VERSION(2,1,0) /* 0x20100 */ + pcibios_read_config_dword (pciBus, pciDevFun, + PCI_BASE_ADDRESS_0, + (u_int *) & megaBase); + pcibios_read_config_byte (pciBus, pciDevFun, + PCI_INTERRUPT_LINE, &megaIrq); +#elif LINUX_VERSION_CODE < KERNEL_VERSION(2,3,0) /*0x20300 */ + megaBase = pdev->base_address[0]; + megaIrq = pdev->irq; +#else + + megaBase = pci_resource_start (pdev, 0); + megaIrq = pdev->irq; +#endif + + pciIdx++; + + if (flag & BOARD_QUARTZ) { + megaBase &= PCI_BASE_ADDRESS_MEM_MASK; + megaBase = (long) ioremap (megaBase, 128); + if (!megaBase) + continue; + } else { + megaBase &= PCI_BASE_ADDRESS_IO_MASK; + megaBase += 0x10; + } + + /* Initialize SCSI Host structure */ + host = scsi_register (pHostTmpl, sizeof (mega_host_config)); + if (!host) + goto err_unmap; + + /* + * Comment the following initialization if you know 'max_sectors' is + * not defined for this kernel. + * This field was introduced in Linus's kernel 2.4.7pre3 and it + * greatly increases the IO performance - AM + */ + host->max_sectors = 1024; + + scsi_set_pci_device(host, pdev); + megaCfg = (mega_host_config *) host->hostdata; + memset (megaCfg, 0, sizeof (mega_host_config)); + + printk (KERN_NOTICE "scsi%d : Found a MegaRAID controller at 0x%x, IRQ: %d" + M_RD_CRLFSTR, host->host_no, (u_int) megaBase, megaIrq); + + if (flag & BOARD_64BIT) + printk (KERN_NOTICE "scsi%d : Enabling 64 bit support\n", + host->host_no); + + /* Copy resource info into structure */ + megaCfg->qCompletedH = NULL; + megaCfg->qCompletedT = NULL; + megaCfg->qPendingH = NULL; + megaCfg->qPendingT = NULL; + megaCfg->qFreeH = NULL; + megaCfg->qFreeT = NULL; + megaCfg->qFcnt = 0; + megaCfg->qPcnt = 0; + megaCfg->qCcnt = 0; + megaCfg->lock_free = SPIN_LOCK_UNLOCKED; + megaCfg->lock_pend = SPIN_LOCK_UNLOCKED; + megaCfg->lock_scsicmd = SPIN_LOCK_UNLOCKED; + megaCfg->flag = flag; + megaCfg->int_qh = NULL; + megaCfg->int_qt = NULL; + megaCfg->int_qlen = 0; + +#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,4,0) + megaCfg->dev = pdev; +#endif + megaCfg->host = host; + megaCfg->base = megaBase; + megaCfg->host->irq = megaIrq; + megaCfg->host->io_port = megaBase; + megaCfg->host->n_io_port = 16; + megaCfg->host->unique_id = (pciBus << 8) | pciDevFun; + megaCtlrs[numCtlrs] = megaCfg; + + if (!(flag & BOARD_QUARTZ)) { + + /* Request our IO Range */ + if( !request_region(megaBase, 16, "megaraid") ) + goto err_unregister; + } + + /* Request our IRQ */ + if (request_irq (megaIrq, megaraid_isr, SA_SHIRQ, + "megaraid", megaCfg)) { + printk (KERN_WARNING + "megaraid: Couldn't register IRQ %d!\n", + megaIrq); + goto err_release; + } + +#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,4,0) + /* + * unmap while releasing the driver, Is it required to be + * PCI_DMA_BIDIRECTIONAL + */ + + megaCfg->mailbox64ptr + = pci_alloc_consistent (megaCfg->dev, + sizeof (mega_mailbox64), + &(megaCfg->dma_handle64)); + + mega_register_mailbox (megaCfg, + virt_to_bus ((void *) megaCfg-> + mailbox64ptr)); +#else + mega_register_mailbox (megaCfg, + virt_to_bus ((void *) &megaCfg-> + mailbox64)); +#endif + + mega_i_query_adapter (megaCfg); + + if ((subsysid == 0x1111) && (subsysvid == 0x1111)) { + + /* + * Which firmware + */ + if( strcmp(megaCfg->fwVer, "3.00") == 0 || + strcmp(megaCfg->fwVer, "3.01") == 0 ) { + + printk( KERN_WARNING + "megaraid: Your card is a Dell PERC 2/SC RAID controller " + "with firmware\nmegaraid: 3.00 or 3.01. This driver is " + "known to have corruption issues\nmegaraid: with those " + "firmware versions on this specific card. In order\n" + "megaraid: to protect your data, please upgrade your " + "firmware to version\nmegaraid: 3.10 or later, available " + "from the Dell Technical Support web\nmegaraid: site at\n" + "http://support.dell.com/us/en/filelib/download/" + "index.asp?fileid=2940\n" + ); + } + } + + /* + * If we have a HP 1M(0x60E7)/2M(0x60E8) controller with + * firmware H.01.07 or H.01.08, disable 64 bit support, + * since this firmware cannot handle 64 bit addressing + */ + + if( (subsysvid == HP_SUBSYS_ID) && + ((subsysid == 0x60E7)||(subsysid == 0x60E8)) ) { + + /* + * which firmware + */ + if( strcmp(megaCfg->fwVer, "H01.07") == 0 || + strcmp(megaCfg->fwVer, "H01.08") == 0 || + strcmp(megaCfg->fwVer, "H01.09") == 0 ) + { + printk(KERN_WARNING + "megaraid: Firmware H.01.07/8/9 on 1M/2M " + "controllers\nmegaraid: do not support 64 bit " + "addressing.\n" + "megaraid: DISABLING 64 bit support.\n"); + megaCfg->flag &= ~BOARD_64BIT; + } + } + + if (mega_is_bios_enabled (megaCfg)) { + mega_hbas[numCtlrs].is_bios_enabled = 1; + } + + /* + * Find out which channel is raid and which is scsi + */ + mega_enum_raid_scsi(megaCfg); + + /* + * Find out if a logical drive is set as the boot drive. If there is + * one, will make that as the first logical drive. + * ROMB: Do we have to boot from a physical drive. Then all the + * physical drives would appear before the logical disks. Else, all + * the physical drives would be exported to the mid layer after + * logical disks. + */ + mega_get_boot_drv(megaCfg); + + if( ! megaCfg->boot_pdrv_enabled ) { + for( i = 0; i < NVIRT_CHAN; i++ ) + megaCfg->logdrv_chan[i] = 1; + + for( i = NVIRT_CHAN; i < MAX_CHANNEL + NVIRT_CHAN; i++ ) + megaCfg->logdrv_chan[i] = 0; + + megaCfg->mega_ch_class <<= NVIRT_CHAN; + } + else { + j = megaCfg->productInfo.SCSIChanPresent; + for( i = 0; i < j; i++ ) + megaCfg->logdrv_chan[i] = 0; + + for( i = j; i < NVIRT_CHAN + j; i++ ) + megaCfg->logdrv_chan[i] = 1; + } + + + mega_hbas[numCtlrs].hostdata_addr = megaCfg; + + /* + * Do we support random deletion and addition of logical drives + */ + megaCfg->read_ldidmap = 0; /* set it after first logdrv delete cmd */ + megaCfg->support_random_del = mega_support_random_del(megaCfg); + + /* Initialize SCBs */ + if (mega_init_scb (megaCfg)) { + pci_free_consistent (megaCfg->dev, + sizeof (mega_mailbox64), + (void *) megaCfg->mailbox64ptr, + megaCfg->dma_handle64); + scsi_unregister (host); + continue; + } + + /* + * Fill in the structure which needs to be passed back to the + * application when it does an ioctl() for controller related + * information. + */ + + i = numCtlrs; + numCtlrs++; + + mcontroller[i].base = megaBase; + mcontroller[i].irq = megaIrq; + mcontroller[i].numldrv = megaCfg->numldrv; + mcontroller[i].pcibus = pciBus; + mcontroller[i].pcidev = pciDev; + mcontroller[i].pcifun = PCI_FUNC (pciDevFun); + mcontroller[i].pciid = pciIdx; + mcontroller[i].pcivendor = pciVendor; + mcontroller[i].pcislot = PCI_SLOT (pciDevFun); + mcontroller[i].uid = (pciBus << 8) | pciDevFun; + + numFound++; + + /* Set the Mode of addressing to 64 bit */ +#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,4,0) + if ((megaCfg->flag & BOARD_64BIT) && BITS_PER_LONG == 64) +#ifdef __LP64__ + pdev->dma_mask = 0xffffffffffffffff; +#else + pdev->dma_mask = 0xffffffff; +#endif +#endif + continue; + err_release: + if (flag & BOARD_QUARTZ) + release_region (megaBase, 16); + err_unregister: + scsi_unregister (host); + err_unmap: + if (flag & BOARD_QUARTZ) + iounmap ((void *) megaBase); + } + return numFound; +} + +/*--------------------------------------------------------- + * Detects if a megaraid controller exists in this system + *---------------------------------------------------------*/ + +int megaraid_detect (Scsi_Host_Template * pHostTmpl) +{ +#if XENO_KILLED + int ctlridx = 0; +#endif + int count = 0; + +#if LINUX_VERSION_CODE < KERNEL_VERSION(2,3,0) /*0x20300 */ + pHostTmpl->proc_dir = &proc_scsi_megaraid; +#else + pHostTmpl->proc_name = "megaraid"; +#endif + +#if LINUX_VERSION_CODE < KERNEL_VERSION(2,1,0) /* 0x20100 */ + if (!pcibios_present ()) { + printk (KERN_WARNING "megaraid: PCI bios not present." + M_RD_CRLFSTR); + return 0; + } +#endif + skip_id = -1; + if (megaraid && !strncmp (megaraid, "skip", strlen ("skip"))) { + if (megaraid[4] != '\0') { + skip_id = megaraid[4] - '0'; + if (megaraid[5] != '\0') { + skip_id = (skip_id * 10) + (megaraid[5] - '0'); + } + } + skip_id = (skip_id > 15) ? -1 : skip_id; + } + + printk (KERN_NOTICE "megaraid: " MEGARAID_VERSION); + + memset (mega_hbas, 0, sizeof (mega_hbas)); + + /* Detect ROMBs first */ + count += mega_findCard (pHostTmpl, PCI_VENDOR_ID_DISCOVERY, + PCI_DEVICE_ID_DISCOVERY, BOARD_QUARTZ); + count += mega_findCard (pHostTmpl, PCI_VENDOR_ID_PERC4_DI_YSTONE, + PCI_DEVICE_ID_PERC4_DI_YSTONE, BOARD_QUARTZ); + /* Then detect cards based on date they were produced, oldest first */ + count += mega_findCard (pHostTmpl, PCI_VENDOR_ID_AMI, + PCI_DEVICE_ID_AMI_MEGARAID, 0); + count += mega_findCard (pHostTmpl, PCI_VENDOR_ID_AMI, + PCI_DEVICE_ID_AMI_MEGARAID2, 0); + count += mega_findCard (pHostTmpl, 0x8086, + PCI_DEVICE_ID_AMI_MEGARAID3, BOARD_QUARTZ); + count += mega_findCard (pHostTmpl, PCI_VENDOR_ID_AMI, + PCI_DEVICE_ID_AMI_MEGARAID3, BOARD_QUARTZ); + count += mega_findCard (pHostTmpl, PCI_VENDOR_ID_LSI_LOGIC, + PCI_DEVICE_ID_AMI_MEGARAID3, BOARD_QUARTZ); + count += mega_findCard (pHostTmpl, PCI_VENDOR_ID_PERC4_QC_VERDE, + PCI_DEVICE_ID_PERC4_QC_VERDE, BOARD_QUARTZ); + + mega_reorder_hosts (); + +#ifdef CONFIG_PROC_FS + if (count) { +#if LINUX_VERSION_CODE > KERNEL_VERSION(2,3,0) /*0x20300 */ + mega_proc_dir_entry = proc_mkdir ("megaraid", &proc_root); +#else + mega_proc_dir_entry = create_proc_entry ("megaraid", + S_IFDIR | S_IRUGO | + S_IXUGO, &proc_root); +#endif + if (!mega_proc_dir_entry) + printk ("megaraid: failed to create megaraid root\n"); + else + for (ctlridx = 0; ctlridx < count; ctlridx++) + mega_create_proc_entry (ctlridx, + mega_proc_dir_entry); + } +#endif + + /* + * Register the driver as a character device, for applications to access + * it for ioctls. + * Ideally, this should go in the init_module() routine, but since it is + * hidden in the file "scsi_module.c" ( included in the end ), we define + * it here + * First argument (major) to register_chrdev implies a dynamic major + * number allocation. + */ +#if XENO_KILLED + if (count) { + major = register_chrdev (0, "megadev", &megadev_fops); + + /* + * Register the Shutdown Notification hook in kernel + */ + if (register_reboot_notifier (&mega_notifier)) { + printk ("MegaRAID Shutdown routine not registered!!\n"); + } + + init_MUTEX (&mimd_entry_mtx); + } +#endif + + return count; +} + +/*--------------------------------------------------------------------- + * Release the controller's resources + *---------------------------------------------------------------------*/ +int megaraid_release (struct Scsi_Host *pSHost) +{ + mega_host_config *megaCfg; + mega_mailbox *mbox; + u_char mboxData[16]; +#ifdef CONFIG_PROC_FS + int i; +#endif + + megaCfg = (mega_host_config *) pSHost->hostdata; + mbox = (mega_mailbox *) mboxData; + + /* Flush cache to disk */ + memset (mbox, 0, 16); + mboxData[0] = 0xA; + + free_irq (megaCfg->host->irq, megaCfg); /* Must be freed first, otherwise + extra interrupt is generated */ + + /* Issue a blocking (interrupts disabled) command to the card */ + megaIssueCmd (megaCfg, mboxData, NULL, 0); + + /* Free our resources */ + if (megaCfg->flag & BOARD_QUARTZ) { + iounmap ((void *) megaCfg->base); + } else { + release_region (megaCfg->host->io_port, 16); + } + + mega_freeSgList (megaCfg); + pci_free_consistent (megaCfg->dev, + sizeof (mega_mailbox64), + (void *) megaCfg->mailbox64ptr, + megaCfg->dma_handle64); + +#ifdef CONFIG_PROC_FS + if (megaCfg->controller_proc_dir_entry) { + remove_proc_entry ("stat", megaCfg->controller_proc_dir_entry); + remove_proc_entry ("status", + megaCfg->controller_proc_dir_entry); + remove_proc_entry ("config", + megaCfg->controller_proc_dir_entry); + remove_proc_entry ("mailbox", + megaCfg->controller_proc_dir_entry); + for (i = 0; i < numCtlrs; i++) { + char buf[12] = { 0 }; + sprintf (buf, "%d", i); + remove_proc_entry (buf, mega_proc_dir_entry); + } + remove_proc_entry ("megaraid", &proc_root); + } +#endif + + /* + * Release the controller memory. A word of warning this frees + * hostdata and that includes megaCfg-> so be careful what you + * dereference beyond this point + */ + + scsi_unregister (pSHost); + + /* + * Unregister the character device interface to the driver. Ideally this + * should have been done in cleanup_module routine. Since this is hidden + * in file "scsi_module.c", we do it here. + * major is the major number of the character device returned by call to + * register_chrdev() routine. + */ + +#if XENO_KILLED + unregister_chrdev (major, "megadev"); + unregister_reboot_notifier (&mega_notifier); +#endif + + return 0; +} + +static int mega_is_bios_enabled (mega_host_config * megacfg) +{ + mega_mailbox *mboxpnt; + unsigned char mbox[16]; + int ret; + + mboxpnt = (mega_mailbox *) mbox; + + memset (mbox, 0, sizeof (mbox)); + memset ((void *) megacfg->mega_buffer, + 0, sizeof (megacfg->mega_buffer)); + + /* + * issue command to find out if the BIOS is enabled for this controller + */ + mbox[0] = IS_BIOS_ENABLED; + mbox[2] = GET_BIOS; + + mboxpnt->xferaddr = virt_to_bus ((void *) megacfg->mega_buffer); + + ret = megaIssueCmd (megacfg, mbox, NULL, 0); + + return (*(char *) megacfg->mega_buffer); +} + +/* + * Find out what channels are RAID/SCSI + */ +static void +mega_enum_raid_scsi(mega_host_config *megacfg) +{ + mega_mailbox *mboxp; + unsigned char mbox[16]; + int i; + +#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,4,0) + dma_addr_t dma_handle; +#endif + + mboxp = (mega_mailbox *)mbox; + + memset(mbox, 0, sizeof(mbox)); + /* + * issue command to find out what channels are raid/scsi + */ + mbox[0] = CHNL_CLASS; + mbox[2] = GET_CHNL_CLASS; + + memset((void *)megacfg->mega_buffer, 0, sizeof(megacfg->mega_buffer)); + +#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,4,0) + dma_handle = pci_map_single(megacfg->dev, (void *)megacfg->mega_buffer, + (2 * 1024L), PCI_DMA_FROMDEVICE); + + mboxp->xferaddr = dma_handle; +#else + mboxp->xferaddr = virt_to_bus((void *)megacfg->mega_buffer); +#endif + + /* + * Non-ROMB firware fail this command, so all channels + * must be shown RAID + */ + megacfg->mega_ch_class = 0xFF; + if( megaIssueCmd(megacfg, mbox, NULL, 0) == 0 ) { + megacfg->mega_ch_class = *((char *)megacfg->mega_buffer); + } + + for( i = 0; i < megacfg->productInfo.SCSIChanPresent; i++ ) { + if( (megacfg->mega_ch_class >> i) & 0x01 ) + printk(KERN_NOTICE"megaraid: channel[%d] is raid.\n", i+1); + else + printk(KERN_NOTICE"megaraid: channel[%d] is scsi.\n", i+1); + } + + +#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,4,0) + pci_unmap_single(megacfg->dev, dma_handle, + (2 * 1024L), PCI_DMA_FROMDEVICE); +#endif + +} + + +/* + * get the boot logical drive number if enabled + */ +void +mega_get_boot_drv(mega_host_config *megacfg) +{ + mega_mailbox *mboxp; + unsigned char mbox[16]; + struct private_bios_data *prv_bios_data; + u16 cksum = 0; + u8 *cksum_p; + u8 boot_pdrv; + int i; + +#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,4,0) + dma_addr_t dma_handle; +#endif + + mboxp = (mega_mailbox *)mbox; + + memset(mbox, 0, sizeof(mbox)); + + mbox[0] = BIOS_PVT_DATA; + mbox[2] = GET_BIOS_PVT_DATA; + + memset((void *)megacfg->mega_buffer, 0, sizeof(megacfg->mega_buffer)); + +#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,4,0) + dma_handle = pci_map_single(megacfg->dev, (void *)megacfg->mega_buffer, + (2 * 1024L), PCI_DMA_FROMDEVICE); + + mboxp->xferaddr = dma_handle; +#else + mboxp->xferaddr = virt_to_bus((void *)megacfg->mega_buffer); +#endif + + megacfg->boot_ldrv_enabled = 0; + megacfg->boot_ldrv = 0; + + megacfg->boot_pdrv_enabled = 0; + megacfg->boot_pdrv_ch = 0; + megacfg->boot_pdrv_tgt = 0; + + if( megaIssueCmd(megacfg, mbox, NULL, 0) == 0 ) { + prv_bios_data = (struct private_bios_data *)megacfg->mega_buffer; + + cksum = 0; + cksum_p = (u8 *)prv_bios_data; + for( i = 0; i < 14; i++ ) { + cksum += *cksum_p++; + } + + if( prv_bios_data->cksum == (u16)(0-cksum) ) { + + /* + * If MSB is set, a physical drive is set as boot device + */ + if( prv_bios_data->boot_drv & 0x80 ) { + megacfg->boot_pdrv_enabled = 1; + boot_pdrv = prv_bios_data->boot_drv & 0x7F; + megacfg->boot_pdrv_ch = boot_pdrv / 16; + megacfg->boot_pdrv_tgt = boot_pdrv % 16; + } + else { + megacfg->boot_ldrv_enabled = 1; + megacfg->boot_ldrv = prv_bios_data->boot_drv; + } + } + } + +#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,4,0) + pci_unmap_single(megacfg->dev, dma_handle, + (2 * 1024L), PCI_DMA_FROMDEVICE); +#endif + +} + + +static void mega_reorder_hosts (void) +{ + struct Scsi_Host *shpnt; + struct Scsi_Host *shone; + struct Scsi_Host *shtwo; + mega_host_config *boot_host; + int i; + + /* + * Find the (first) host which has it's BIOS enabled + */ + boot_host = NULL; + for (i = 0; i < MAX_CONTROLLERS; i++) { + if (mega_hbas[i].is_bios_enabled) { + boot_host = mega_hbas[i].hostdata_addr; + break; + } + } + + if (boot_host == NULL) { + printk (KERN_WARNING "megaraid: no BIOS enabled.\n"); + return; + } + + /* + * Traverse through the list of SCSI hosts for our HBA locations + */ + shone = shtwo = NULL; + for (shpnt = scsi_hostlist; shpnt; shpnt = shpnt->next) { + /* Is it one of ours? */ + for (i = 0; i < MAX_CONTROLLERS; i++) { + if ((mega_host_config *) shpnt->hostdata == + mega_hbas[i].hostdata_addr) { + /* Does this one has BIOS enabled */ + if (mega_hbas[i].hostdata_addr == boot_host) { + + /* Are we first */ + if (shtwo == NULL) /* Yes! */ + return; + else { /* :-( */ + shone = shpnt; + } + } else { + if (!shtwo) { + /* were we here before? xchng first */ + shtwo = shpnt; + } + } + break; + } + } + /* + * Have we got the boot host and one which does not have the bios + * enabled. + */ + if (shone && shtwo) + break; + } + if (shone && shtwo) { + mega_swap_hosts (shone, shtwo); + } + + return; +} + +static void mega_swap_hosts (struct Scsi_Host *shone, struct Scsi_Host *shtwo) +{ + struct Scsi_Host *prevtoshtwo; + struct Scsi_Host *prevtoshone; + struct Scsi_Host *save = NULL;; + + /* Are these two nodes adjacent */ + if (shtwo->next == shone) { + + if (shtwo == scsi_hostlist && shone->next == NULL) { + + /* just two nodes */ + scsi_hostlist = shone; + shone->next = shtwo; + shtwo->next = NULL; + } else if (shtwo == scsi_hostlist) { + /* first two nodes of the list */ + + scsi_hostlist = shone; + shtwo->next = shone->next; + scsi_hostlist->next = shtwo; + } else if (shone->next == NULL) { + /* last two nodes of the list */ + + prevtoshtwo = scsi_hostlist; + + while (prevtoshtwo->next != shtwo) + prevtoshtwo = prevtoshtwo->next; + + prevtoshtwo->next = shone; + shone->next = shtwo; + shtwo->next = NULL; + } else { + prevtoshtwo = scsi_hostlist; + + while (prevtoshtwo->next != shtwo) + prevtoshtwo = prevtoshtwo->next; + + prevtoshtwo->next = shone; + shtwo->next = shone->next; + shone->next = shtwo; + } + + } else if (shtwo == scsi_hostlist && shone->next == NULL) { + /* shtwo at head, shone at tail, not adjacent */ + + prevtoshone = scsi_hostlist; + + while (prevtoshone->next != shone) + prevtoshone = prevtoshone->next; + + scsi_hostlist = shone; + shone->next = shtwo->next; + prevtoshone->next = shtwo; + shtwo->next = NULL; + } else if (shtwo == scsi_hostlist && shone->next != NULL) { + /* shtwo at head, shone is not at tail */ + + prevtoshone = scsi_hostlist; + while (prevtoshone->next != shone) + prevtoshone = prevtoshone->next; + + scsi_hostlist = shone; + prevtoshone->next = shtwo; + save = shtwo->next; + shtwo->next = shone->next; + shone->next = save; + } else if (shone->next == NULL) { + /* shtwo not at head, shone at tail */ + + prevtoshtwo = scsi_hostlist; + prevtoshone = scsi_hostlist; + + while (prevtoshtwo->next != shtwo) + prevtoshtwo = prevtoshtwo->next; + while (prevtoshone->next != shone) + prevtoshone = prevtoshone->next; + + prevtoshtwo->next = shone; + shone->next = shtwo->next; + prevtoshone->next = shtwo; + shtwo->next = NULL; + + } else { + prevtoshtwo = scsi_hostlist; + prevtoshone = scsi_hostlist; + save = NULL;; + + while (prevtoshtwo->next != shtwo) + prevtoshtwo = prevtoshtwo->next; + while (prevtoshone->next != shone) + prevtoshone = prevtoshone->next; + + prevtoshtwo->next = shone; + save = shone->next; + shone->next = shtwo->next; + prevtoshone->next = shtwo; + shtwo->next = save; + } + return; +} + +static inline void mega_freeSgList (mega_host_config * megaCfg) +{ + int i; + + for (i = 0; i < megaCfg->max_cmds; i++) { + if (megaCfg->scbList[i].sgList) + pci_free_consistent (megaCfg->dev, + sizeof (mega_64sglist) * + MAX_SGLIST, + megaCfg->scbList[i].sgList, + megaCfg->scbList[i]. + dma_sghandle64); +#if LINUX_VERSION_CODE < KERNEL_VERSION(2,4,0) /* 0x020400 */ + kfree (megaCfg->scbList[i].sgList); /* free sgList */ +#endif + } +} + +/*---------------------------------------------- + * Get information about the card/driver + *----------------------------------------------*/ +const char *megaraid_info (struct Scsi_Host *pSHost) +{ + static char buffer[512]; + mega_host_config *megaCfg; + + megaCfg = (mega_host_config *) pSHost->hostdata; + + sprintf (buffer, + "LSI Logic MegaRAID %s %d commands %d targs %d chans %d luns", + megaCfg->fwVer, megaCfg->productInfo.MaxConcCmds, + megaCfg->host->max_id-1, megaCfg->host->max_channel, + megaCfg->host->max_lun); + return buffer; +} + +/*----------------------------------------------------------------- + * Perform a SCSI command + * Mailbox area: + * 00 01 command + * 01 01 command id + * 02 02 # of sectors + * 04 04 logical bus address + * 08 04 physical buffer address + * 0C 01 logical drive # + * 0D 01 length of scatter/gather list + * 0E 01 reserved + * 0F 01 mailbox busy + * 10 01 numstatus byte + * 11 01 status byte + *-----------------------------------------------------------------*/ +int megaraid_queue (Scsi_Cmnd * SCpnt, void (*pktComp) (Scsi_Cmnd *)) +{ + DRIVER_LOCK_T mega_host_config * megaCfg; + mega_scb *pScb; + char *user_area = NULL; + + megaCfg = (mega_host_config *) SCpnt->host->hostdata; + DRIVER_LOCK (megaCfg); + +#if 0 + if (!(megaCfg->flag & (1L << SCpnt->channel))) { + if (SCpnt->channel < megaCfg->productInfo.SCSIChanPresent) + printk ( KERN_NOTICE + "scsi%d: scanning channel %d for devices.\n", + megaCfg->host->host_no, SCpnt->channel); + else + printk ( KERN_NOTICE + "scsi%d: scanning virtual channel %d for logical drives.\n", + megaCfg->host->host_no, + SCpnt->channel-megaCfg->productInfo.SCSIChanPresent+1); + + megaCfg->flag |= (1L << SCpnt->channel); + } +#endif + + SCpnt->scsi_done = pktComp; + + if (mega_driver_ioctl (megaCfg, SCpnt)) + return 0; + + /* If driver in abort or reset.. cancel this command */ + if (megaCfg->flag & IN_ABORT) { + SCpnt->result = (DID_ABORT << 16); + /* Add Scsi_Command to end of completed queue */ + if (megaCfg->qCompletedH == NULL) { + megaCfg->qCompletedH = megaCfg->qCompletedT = SCpnt; + } else { + megaCfg->qCompletedT->host_scribble = + (unsigned char *) SCpnt; + megaCfg->qCompletedT = SCpnt; + } + megaCfg->qCompletedT->host_scribble = (unsigned char *) NULL; + megaCfg->qCcnt++; + + DRIVER_UNLOCK (megaCfg); + return 0; + } else if (megaCfg->flag & IN_RESET) { + SCpnt->result = (DID_RESET << 16); + /* Add Scsi_Command to end of completed queue */ + if (megaCfg->qCompletedH == NULL) { + megaCfg->qCompletedH = megaCfg->qCompletedT = SCpnt; + } else { + megaCfg->qCompletedT->host_scribble = + (unsigned char *) SCpnt; + megaCfg->qCompletedT = SCpnt; + } + megaCfg->qCompletedT->host_scribble = (unsigned char *) NULL; + megaCfg->qCcnt++; + + DRIVER_UNLOCK (megaCfg); + return 0; + } + + megaCfg->flag |= IN_QUEUE; + /* Allocate and build a SCB request */ + if ((pScb = mega_build_cmd (megaCfg, SCpnt)) != NULL) { + + /* + * Check if the HBA is in quiescent state, e.g., during a delete + * logical drive opertion. If it is, queue the commands in the + * internal queue until the delete operation is complete. + */ + if( ! megaCfg->quiescent ) { + /* Add SCB to the head of the pending queue */ + if (megaCfg->qPendingH == NULL) { + megaCfg->qPendingH = megaCfg->qPendingT = pScb; + } else { + megaCfg->qPendingT->next = pScb; + megaCfg->qPendingT = pScb; + } + megaCfg->qPendingT->next = NULL; + megaCfg->qPcnt++; + + if (mega_runpendq (megaCfg) == -1) { + DRIVER_UNLOCK (megaCfg); + return 0; + } + } + else { + /* Add SCB to the internal queue */ + if (megaCfg->int_qh == NULL) { + megaCfg->int_qh = megaCfg->int_qt = pScb; + } else { + megaCfg->int_qt->next = pScb; + megaCfg->int_qt = pScb; + } + megaCfg->int_qt->next = NULL; + megaCfg->int_qlen++; + } + + if (pScb->SCpnt->cmnd[0] == M_RD_IOCTL_CMD_NEW) { +#if XENO_KILLED + init_MUTEX_LOCKED (&pScb->ioctl_sem); +#endif + spin_unlock_irq (&io_request_lock); +#if XENO_KILLED + down (&pScb->ioctl_sem); +#endif + user_area = (char *)*((u32*)&pScb->SCpnt->cmnd[4]); + if (copy_to_user + (user_area, pScb->buff_ptr, pScb->iDataSize)) { + printk + ("megaraid: Error copying ioctl return value to user buffer.\n"); + pScb->SCpnt->result = (DID_ERROR << 16); + } + spin_lock_irq (&io_request_lock); + DRIVER_LOCK (megaCfg); + kfree (pScb->buff_ptr); + pScb->buff_ptr = NULL; + mega_cmd_done (megaCfg, pScb, pScb->SCpnt->result); + mega_rundoneq (megaCfg); + mega_runpendq (megaCfg); + DRIVER_UNLOCK (megaCfg); + } + + megaCfg->flag &= ~IN_QUEUE; + + } + + DRIVER_UNLOCK (megaCfg); + return 0; +} + +/*---------------------------------------------------------------------- + * Issue a blocking command to the controller + *----------------------------------------------------------------------*/ +#if XENO_KILLED +volatile static int internal_done_flag = 0; +volatile static int internal_done_errcode = 0; + +static DECLARE_WAIT_QUEUE_HEAD (internal_wait); + +static void internal_done (Scsi_Cmnd * SCpnt) +{ + internal_done_errcode = SCpnt->result; + internal_done_flag++; + wake_up (&internal_wait); +} + +/* shouldn't be used, but included for completeness */ + +int megaraid_command (Scsi_Cmnd * SCpnt) +{ + internal_done_flag = 0; + + /* Queue command, and wait until it has completed */ + megaraid_queue (SCpnt, internal_done); + + while (!internal_done_flag) { + interruptible_sleep_on (&internal_wait); + } + + return internal_done_errcode; +} +#endif + +/*--------------------------------------------------------------------- + * Abort a previous SCSI request + *---------------------------------------------------------------------*/ +int megaraid_abort (Scsi_Cmnd * SCpnt) +{ + mega_host_config *megaCfg; + int rc; /*, idx; */ + mega_scb *pScb; + + rc = SCSI_ABORT_NOT_RUNNING; + + megaCfg = (mega_host_config *) SCpnt->host->hostdata; + + megaCfg->flag |= IN_ABORT; + + for (pScb = megaCfg->qPendingH; pScb; pScb = pScb->next) { + if (pScb->SCpnt == SCpnt) { + /* Found an aborting command */ +#if DEBUG + showMbox (pScb); +#endif + + /* + * If the command is queued to be issued to the firmware, abort the scsi cmd, + * If the command is already aborted in a previous call to the _abort entry + * point, return SCSI_ABORT_SNOOZE, suggesting a reset. + * If the command is issued to the firmware, which might complete after + * some time, we will mark the scb as aborted, and return to the mid layer, + * that abort could not be done. + * In the ISR, when this command actually completes, we will perform a normal + * completion. + * + * Oct 27, 1999 + */ + + switch (pScb->state) { + case SCB_ABORTED: /* Already aborted */ + rc = SCSI_ABORT_SNOOZE; + break; + case SCB_ISSUED: /* Waiting on ISR result */ + rc = SCSI_ABORT_NOT_RUNNING; + pScb->state = SCB_ABORTED; + break; + case SCB_ACTIVE: /* still on the pending queue */ + mega_freeSCB (megaCfg, pScb); + SCpnt->result = (DID_ABORT << 16); + if (megaCfg->qCompletedH == NULL) { + megaCfg->qCompletedH = + megaCfg->qCompletedT = SCpnt; + } else { + megaCfg->qCompletedT->host_scribble = + (unsigned char *) SCpnt; + megaCfg->qCompletedT = SCpnt; + } + megaCfg->qCompletedT->host_scribble = + (unsigned char *) NULL; + megaCfg->qCcnt++; + rc = SCSI_ABORT_SUCCESS; + break; + default: + printk + ("megaraid_abort: unknown command state!!\n"); + rc = SCSI_ABORT_NOT_RUNNING; + break; + } + break; + } + } + + megaCfg->flag &= ~IN_ABORT; + +#if DEBUG + if (megaCfg->flag & IN_QUEUE) + printk ("ma:flag is in queue\n"); + if (megaCfg->qCompletedH == NULL) + printk ("ma:qchead == null\n"); +#endif + + /* + * This is required here to complete any completed requests to be communicated + * over to the mid layer. + * Calling just mega_rundoneq() did not work. + */ + if (megaCfg->qCompletedH) { + SCpnt = megaCfg->qCompletedH; + megaCfg->qCompletedH = (Scsi_Cmnd *) SCpnt->host_scribble; + megaCfg->qCcnt--; + + SCpnt->host_scribble = (unsigned char *) NULL; + /* Callback */ + callDone (SCpnt); + } + mega_rundoneq (megaCfg); + + return rc; +} + +/*--------------------------------------------------------------------- + * Reset a previous SCSI request + *---------------------------------------------------------------------*/ + +int megaraid_reset (Scsi_Cmnd * SCpnt, unsigned int rstflags) +{ + mega_host_config *megaCfg; + int idx; + int rc; + mega_scb *pScb; + + rc = SCSI_RESET_NOT_RUNNING; + megaCfg = (mega_host_config *) SCpnt->host->hostdata; + + megaCfg->flag |= IN_RESET; + + printk + ("megaraid_RESET: %.08lx cmd=%.02x , flag = %x\n", + SCpnt->serial_number, SCpnt->cmnd[0], SCpnt->channel, + SCpnt->target, SCpnt->lun, rstflags); + + TRACE (("RESET: %.08lx %.02x <%d.%d.%d>\n", + SCpnt->serial_number, SCpnt->cmnd[0], SCpnt->channel, + SCpnt->target, SCpnt->lun)); + + /* + * Walk list of SCBs for any that are still outstanding + */ + for (idx = 0; idx < megaCfg->max_cmds; idx++) { + if (megaCfg->scbList[idx].state != SCB_FREE) { + SCpnt = megaCfg->scbList[idx].SCpnt; + pScb = &megaCfg->scbList[idx]; + if (SCpnt != NULL) { + pScb->state = SCB_RESET; + break; + } + } + } + + megaCfg->flag &= ~IN_RESET; + + mega_rundoneq (megaCfg); + return rc; +} + +#ifdef CONFIG_PROC_FS +/* Following code handles /proc fs */ +static int proc_printf (mega_host_config * megaCfg, const char *fmt, ...) +{ + va_list args; + int i; + + if (megaCfg->procidx > PROCBUFSIZE) + return 0; + + va_start (args, fmt); + i = vsprintf ((megaCfg->procbuf + megaCfg->procidx), fmt, args); + va_end (args); + + megaCfg->procidx += i; + return i; +} + +static int proc_read_config (char *page, char **start, off_t offset, + int count, int *eof, void *data) +{ + + mega_host_config *megaCfg = (mega_host_config *) data; + + *start = page; + + if (megaCfg->productInfo.ProductName[0] != 0) + proc_printf (megaCfg, "%s\n", megaCfg->productInfo.ProductName); + + proc_printf (megaCfg, "Controller Type: "); + + if (megaCfg->flag & BOARD_QUARTZ) + proc_printf (megaCfg, "438/466/467/471/493\n"); + else + proc_printf (megaCfg, "418/428/434\n"); + + if (megaCfg->flag & BOARD_40LD) + proc_printf (megaCfg, + "Controller Supports 40 Logical Drives\n"); + + if (megaCfg->flag & BOARD_64BIT) + proc_printf (megaCfg, + "Controller / Driver uses 64 bit memory addressing\n"); + + proc_printf (megaCfg, "Base = %08x, Irq = %d, ", megaCfg->base, + megaCfg->host->irq); + + proc_printf (megaCfg, "Logical Drives = %d, Channels = %d\n", + megaCfg->numldrv, megaCfg->productInfo.SCSIChanPresent); + + proc_printf (megaCfg, "Version =%s:%s, DRAM = %dMb\n", + megaCfg->fwVer, megaCfg->biosVer, + megaCfg->productInfo.DramSize); + + proc_printf (megaCfg, + "Controller Queue Depth = %d, Driver Queue Depth = %d\n", + megaCfg->productInfo.MaxConcCmds, megaCfg->max_cmds); + COPY_BACK; + return count; +} + +static int proc_read_stat (char *page, char **start, off_t offset, + int count, int *eof, void *data) +{ + int i; + mega_host_config *megaCfg = (mega_host_config *) data; + + *start = page; + + proc_printf (megaCfg, "Statistical Information for this controller\n"); + proc_printf (megaCfg, "Interrupts Collected = %lu\n", + megaCfg->nInterrupts); + + for (i = 0; i < megaCfg->numldrv; i++) { + proc_printf (megaCfg, "Logical Drive %d:\n", i); + + proc_printf (megaCfg, + "\tReads Issued = %lu, Writes Issued = %lu\n", + megaCfg->nReads[i], megaCfg->nWrites[i]); + + proc_printf (megaCfg, + "\tSectors Read = %lu, Sectors Written = %lu\n\n", + megaCfg->nReadBlocks[i], megaCfg->nWriteBlocks[i]); + + } + + COPY_BACK; + return count; +} + +static int proc_read_status (char *page, char **start, off_t offset, + int count, int *eof, void *data) +{ + mega_host_config *megaCfg = (mega_host_config *) data; + *start = page; + + proc_printf (megaCfg, "TBD\n"); + COPY_BACK; + return count; +} + +static int proc_read_mbox (char *page, char **start, off_t offset, + int count, int *eof, void *data) +{ + + mega_host_config *megaCfg = (mega_host_config *) data; + volatile mega_mailbox *mbox = megaCfg->mbox; + + *start = page; + + proc_printf (megaCfg, "Contents of Mail Box Structure\n"); + proc_printf (megaCfg, " Fw Command = 0x%02x\n", mbox->cmd); + proc_printf (megaCfg, " Cmd Sequence = 0x%02x\n", mbox->cmdid); + proc_printf (megaCfg, " No of Sectors= %04d\n", mbox->numsectors); + proc_printf (megaCfg, " LBA = 0x%02x\n", mbox->lba); + proc_printf (megaCfg, " DTA = 0x%08x\n", mbox->xferaddr); + proc_printf (megaCfg, " Logical Drive= 0x%02x\n", mbox->logdrv); + proc_printf (megaCfg, " No of SG Elmt= 0x%02x\n", mbox->numsgelements); + proc_printf (megaCfg, " Busy = %01x\n", mbox->busy); + proc_printf (megaCfg, " Status = 0x%02x\n", mbox->status); + + /* proc_printf(megaCfg, "Dump of MailBox\n"); + for (i = 0; i < 16; i++) + proc_printf(megaCfg, "%02x ",*(mbox + i)); + + proc_printf(megaCfg, "\n\nNumber of Status = %02d\n",mbox->numstatus); + + for (i = 0; i < 46; i++) { + proc_printf(megaCfg,"%02d ",*(mbox + 16 + i)); + if (i%16) + proc_printf(megaCfg,"\n"); + } + + if (!mbox->numsgelements) { + dta = phys_to_virt(mbox->xferaddr); + for (i = 0; i < mbox->numsgelements; i++) + if (dta) { + proc_printf(megaCfg,"Addr = %08x\n", (ulong)*(dta + i)); proc_printf(megaCfg,"Length = %08x\n", + (ulong)*(dta + i + 4)); + } + }*/ + COPY_BACK; + return count; +} + +#if LINUX_VERSION_CODE > KERNEL_VERSION(2,3,0) /*0x20300 */ +#define CREATE_READ_PROC(string, fxn) create_proc_read_entry(string, \ + S_IRUSR | S_IFREG,\ + controller_proc_dir_entry,\ + fxn, megaCfg) +#else +#define CREATE_READ_PROC(string, fxn) create_proc_read_entry(string,S_IRUSR | S_IFREG, controller_proc_dir_entry, fxn, megaCfg) + +static struct proc_dir_entry * +create_proc_read_entry (const char *string, + int mode, + struct proc_dir_entry *parent, + read_proc_t * fxn, mega_host_config * megaCfg) +{ + struct proc_dir_entry *temp = NULL; + + temp = kmalloc (sizeof (struct proc_dir_entry), GFP_KERNEL); + if (!temp) + return NULL; + memset (temp, 0, sizeof (struct proc_dir_entry)); + + if ((temp->name = kmalloc (strlen (string) + 1, GFP_KERNEL)) == NULL) { + kfree (temp); + return NULL; + } + + strcpy ((char *) temp->name, string); + temp->namelen = strlen (string); + temp->mode = mode; /*S_IFREG | S_IRUSR */ ; + temp->data = (void *) megaCfg; + temp->read_proc = fxn; + proc_register (parent, temp); + return temp; +} +#endif + +static void mega_create_proc_entry (int index, struct proc_dir_entry *parent) +{ + u_char string[64] = { 0 }; + mega_host_config *megaCfg = megaCtlrs[index]; + struct proc_dir_entry *controller_proc_dir_entry = NULL; + + sprintf (string, "%d", index); + +#if LINUX_VERSION_CODE > KERNEL_VERSION(2,3,0) /*0x20300 */ + controller_proc_dir_entry = + megaCfg->controller_proc_dir_entry = proc_mkdir (string, parent); +#else + controller_proc_dir_entry = + megaCfg->controller_proc_dir_entry = + create_proc_entry (string, S_IFDIR | S_IRUGO | S_IXUGO, parent); +#endif + + if (!controller_proc_dir_entry) + printk ("\nmegaraid: proc_mkdir failed\n"); + else { + megaCfg->proc_read = + CREATE_READ_PROC ("config", proc_read_config); + megaCfg->proc_status = + CREATE_READ_PROC ("status", proc_read_status); + megaCfg->proc_stat = CREATE_READ_PROC ("stat", proc_read_stat); + megaCfg->proc_mbox = + CREATE_READ_PROC ("mailbox", proc_read_mbox); + } + +} +#endif /* CONFIG_PROC_FS */ + +/*------------------------------------------------------------- + * Return the disk geometry for a particular disk + * Input: + * Disk *disk - Disk geometry + * kdev_t dev - Device node + * int *geom - Returns geometry fields + * geom[0] = heads + * geom[1] = sectors + * geom[2] = cylinders + *-------------------------------------------------------------*/ +int megaraid_biosparam (Disk * disk, kdev_t dev, int *geom) +{ + int heads, sectors, cylinders; + mega_host_config *megaCfg; + + /* Get pointer to host config structure */ + megaCfg = (mega_host_config *) disk->device->host->hostdata; + + if( IS_RAID_CH(megaCfg, disk->device->channel)) { + /* Default heads (64) & sectors (32) */ + heads = 64; + sectors = 32; + cylinders = disk->capacity / (heads * sectors); + + /* Handle extended translation size for logical drives > 1Gb */ + if (disk->capacity >= 0x200000) { + heads = 255; + sectors = 63; + cylinders = disk->capacity / (heads * sectors); + } + + /* return result */ + geom[0] = heads; + geom[1] = sectors; + geom[2] = cylinders; + } + else { + if( mega_partsize(disk, dev, geom) == 0 ) return 0; + + printk(KERN_WARNING + "megaraid: invalid partition on this disk on channel %d\n", + disk->device->channel); + + /* Default heads (64) & sectors (32) */ + heads = 64; + sectors = 32; + cylinders = disk->capacity / (heads * sectors); + + /* Handle extended translation size for logical drives > 1Gb */ + if (disk->capacity >= 0x200000) { + heads = 255; + sectors = 63; + cylinders = disk->capacity / (heads * sectors); + } + + /* return result */ + geom[0] = heads; + geom[1] = sectors; + geom[2] = cylinders; + } + + return 0; +} + +/* + * Function : static int mega_partsize(Disk * disk, kdev_t dev, int *geom) + * + * Purpose : to determine the BIOS mapping used to create the partition + * table, storing the results (cyls, hds, and secs) in geom + * + * Note: Code is picked from scsicam.h + * + * Returns : -1 on failure, 0 on success. + */ +static int +mega_partsize(Disk * disk, kdev_t dev, int *geom) +{ +#if XENO_KILLED + struct buffer_head *bh; + struct partition *p, *largest = NULL; + int i, largest_cyl; + int heads, cyls, sectors; + int capacity = disk->capacity; + + int ma = MAJOR(dev); + int mi = (MINOR(dev) & ~0xf); + + int block = 1024; + + + if(blksize_size[ma]) + block = blksize_size[ma][mi]; + + if(!(bh = bread(MKDEV(ma,mi), 0, block))) + return -1; + + if( *(unsigned short *)(bh->b_data + 510) == 0xAA55 ) { + + for( largest_cyl = -1, p = (struct partition *)(0x1BE + bh->b_data), + i = 0; i < 4; ++i, ++p) { + + if (!p->sys_ind) continue; + + cyls = p->end_cyl + ((p->end_sector & 0xc0) << 2); + + if(cyls >= largest_cyl) { + largest_cyl = cyls; + largest = p; + } + } + } + + if (largest) { + heads = largest->end_head + 1; + sectors = largest->end_sector & 0x3f; + + if (heads == 0 || sectors == 0) { + brelse(bh); + return -1; + } + + cyls = capacity/(heads * sectors); + + geom[0] = heads; + geom[1] = sectors; + geom[2] = cyls; + + brelse(bh); + return 0; + } + + brelse(bh); +#endif + return -1; +} + + +/* + * This routine will be called when the use has done a forced shutdown on the + * system. Flush the Adapter cache, that's the most we can do. + */ +static int megaraid_reboot_notify (struct notifier_block *this, unsigned long code, + void *unused) +{ + struct Scsi_Host *pSHost; + mega_host_config *megaCfg; + mega_mailbox *mbox; + u_char mboxData[16]; + int i; + + if (code == SYS_DOWN || code == SYS_HALT) { + for (i = 0; i < numCtlrs; i++) { + pSHost = megaCtlrs[i]->host; + + megaCfg = (mega_host_config *) pSHost->hostdata; + mbox = (mega_mailbox *) mboxData; + + /* Flush cache to disk */ + memset (mbox, 0, 16); + mboxData[0] = 0xA; + + /* + * Free irq, otherwise extra interrupt is generated + */ + free_irq (megaCfg->host->irq, megaCfg); + + /* + * Issue a blocking (interrupts disabled) command to + * the card + */ + megaIssueCmd (megaCfg, mboxData, NULL, 0); + } + } + return NOTIFY_DONE; +} + +static int mega_init_scb (mega_host_config * megacfg) +{ + int idx; + +#if DEBUG + if (megacfg->max_cmds >= MAX_COMMANDS) { + printk ("megaraid:ctlr max cmds = %x : MAX_CMDS = %x", + megacfg->max_cmds, MAX_COMMANDS); + } +#endif + + for (idx = megacfg->max_cmds - 1; idx >= 0; idx--) { + + megacfg->scbList[idx].idx = idx; + + /* + * ISR will make this flag zero to indicate the command has been + * completed. This is only for user ioctl calls. Rest of the driver + * and the mid-layer operations are not connected with this flag. + */ + +#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,4,0) + megacfg->scbList[idx].sgList = + pci_alloc_consistent (megacfg->dev, + sizeof (mega_64sglist) * MAX_SGLIST, + &(megacfg->scbList[idx]. + dma_sghandle64)); + + megacfg->scbList[idx].sg64List = + (mega_64sglist *) megacfg->scbList[idx].sgList; +#else + megacfg->scbList[idx].sgList = kmalloc (sizeof (mega_sglist) * MAX_SGLIST, GFP_ATOMIC | GFP_DMA); +#endif + + if (megacfg->scbList[idx].sgList == NULL) { + printk (KERN_WARNING + "Can't allocate sglist for id %d\n", idx); + mega_freeSgList (megacfg); + return -1; + } +#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,4,0) + megacfg->scbList[idx].pthru = pci_alloc_consistent (megacfg->dev, + sizeof (mega_passthru), + &(megacfg->scbList[idx]. + dma_passthruhandle64)); + + if (megacfg->scbList[idx].pthru == NULL) { + printk (KERN_WARNING + "Can't allocate passthru for id %d\n", idx); + } + + megacfg->scbList[idx].epthru = + pci_alloc_consistent( + megacfg->dev, sizeof(mega_ext_passthru), + &(megacfg->scbList[idx].dma_ext_passthruhandle64) + ); + + if (megacfg->scbList[idx].epthru == NULL) { + printk (KERN_WARNING + "Can't allocate extended passthru for id %d\n", idx); + } + /* + * Allocate a 256 Byte Bounce Buffer for handling INQ/RD_CAPA + */ + megacfg->scbList[idx].bounce_buffer = pci_alloc_consistent (megacfg->dev, + 256, + &(megacfg->scbList[idx]. + dma_bounce_buffer)); + + if (!megacfg->scbList[idx].bounce_buffer) + printk + ("megaraid: allocation for bounce buffer failed\n"); + + megacfg->scbList[idx].dma_type = M_RD_DMA_TYPE_NONE; +#endif + + if (idx < MAX_COMMANDS) { + /* + * Link to free list + * lock not required since we are loading the driver, so no + * commands possible right now. + */ + enq_scb_freelist (megacfg, &megacfg->scbList[idx], + NO_LOCK, INTR_ENB); + + } + } + + return 0; +} + +/* + * Enqueues a SCB + */ +static void enq_scb_freelist (mega_host_config * megacfg, mega_scb * scb, int lock, + int intr) +{ + + if (lock == INTERNAL_LOCK || intr == INTR_DIS) { + if (intr == INTR_DIS) + spin_lock_irq (&megacfg->lock_free); + else + spin_lock (&megacfg->lock_free); + } + + scb->state = SCB_FREE; + scb->SCpnt = NULL; + + if (megacfg->qFreeH == (mega_scb *) NULL) { + megacfg->qFreeH = megacfg->qFreeT = scb; + } else { + megacfg->qFreeT->next = scb; + megacfg->qFreeT = scb; + } + + megacfg->qFreeT->next = NULL; + megacfg->qFcnt++; + + if (lock == INTERNAL_LOCK || intr == INTR_DIS) { + if (intr == INTR_DIS) + spin_unlock_irq (&megacfg->lock_free); + else + spin_unlock (&megacfg->lock_free); + } +} + +/* + * Routines for the character/ioctl interface to the driver + */ +static int megadev_open (struct inode *inode, struct file *filep) +{ + MOD_INC_USE_COUNT; + return 0; /* success */ +} + +static int megadev_ioctl_entry (struct inode *inode, struct file *filep, + unsigned int cmd, unsigned long arg) +{ + int ret = -1; + + /* + * We do not allow parallel ioctls to the driver as of now. + */ +#if XENO_KILLED // XXX JWS killed the locking! + down (&mimd_entry_mtx); + ret = megadev_ioctl (inode, filep, cmd, arg); + up (&mimd_entry_mtx); +#else + ret = megadev_ioctl (inode, filep, cmd, arg); +#endif + + return ret; + +} + +static int megadev_ioctl (struct inode *inode, struct file *filep, + unsigned int cmd, unsigned long arg) +{ + int adapno; + kdev_t dev; + u32 inlen; + struct uioctl_t ioc; + char *kvaddr = NULL; + int nadap = numCtlrs; + u8 opcode; + u32 outlen; + int ret; + u8 subopcode; + Scsi_Cmnd *scsicmd; + struct Scsi_Host *shpnt; + char *uaddr; + struct uioctl_t *uioc; + dma_addr_t dma_addr; + u32 length; + mega_host_config *megacfg = NULL; +#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,4,0) /* 0x020400 */ + struct pci_dev pdev; + struct pci_dev *pdevp = &pdev; +#else + char *pdevp = NULL; +#endif + IO_LOCK_T; + + if (!inode || !(dev = inode->i_rdev)) + return -EINVAL; + + if (_IOC_TYPE (cmd) != MEGAIOC_MAGIC) + return (-EINVAL); + + /* + * Get the user ioctl structure + */ + ret = verify_area (VERIFY_WRITE, (char *) arg, sizeof (struct uioctl_t)); + + if (ret) + return ret; + + if(copy_from_user (&ioc, (char *) arg, sizeof (struct uioctl_t))) + return -EFAULT; + + /* + * The first call the applications should make is to find out the + * number of controllers in the system. The next logical call should + * be for getting the list of controllers in the system as detected + * by the driver. + */ + + /* + * Get the opcode and subopcode for the commands + */ + opcode = ioc.ui.fcs.opcode; + subopcode = ioc.ui.fcs.subopcode; + + switch (opcode) { + case M_RD_DRIVER_IOCTL_INTERFACE: + switch (subopcode) { + case MEGAIOC_QDRVRVER: /* Query driver version */ + put_user (driver_ver, (u32 *) ioc.data); + return 0; + + case MEGAIOC_QNADAP: /* Get # of adapters */ + put_user (nadap, (int *) ioc.data); + return nadap; + + case MEGAIOC_QADAPINFO: /* Get adapter information */ + /* + * which adapter? + */ + adapno = ioc.ui.fcs.adapno; + + /* + * The adapter numbers do not start with 0, at least in + * the user space. This is just to make sure, 0 is not the + * default value which will refer to adapter 1. So the + * user needs to make use of macros MKADAP() and GETADAP() + * (See megaraid.h) while making ioctl() call. + */ + adapno = GETADAP (adapno); + + if (adapno >= numCtlrs) + return (-ENODEV); + + ret = verify_area (VERIFY_WRITE, + ioc.data, + sizeof (struct mcontroller)); + if (ret) + return ret; + + /* + * Copy struct mcontroller to user area + */ + if (copy_to_user (ioc.data, + mcontroller + adapno, + sizeof (struct mcontroller))) + return -EFAULT; + return 0; + + default: + return (-EINVAL); + + } /* inner switch */ + break; + + case M_RD_IOCTL_CMD_NEW: + + /* + * Deletion of logical drives is only handled in 0x80 commands + */ + if( ioc.mbox[0] == FC_DEL_LOGDRV && ioc.mbox[2] == OP_DEL_LOGDRV ) { + return -EINVAL; + } + + /* which adapter? */ + adapno = ioc.ui.fcs.adapno; + + /* See comment above: MEGAIOC_QADAPINFO */ + adapno = GETADAP(adapno); + + if (adapno >= numCtlrs) + return(-ENODEV); + + length = ioc.ui.fcs.length; + + /* Check for zero length buffer or very large buffers */ + if( !length || length > 32*1024 ) + return -EINVAL; + + /* save the user address */ + uaddr = ioc.ui.fcs.buffer; + + /* + * For M_RD_IOCTL_CMD_NEW commands, the fields outlen and inlen of + * uioctl_t structure are treated as flags. If outlen is 1, the + * data is transferred from the device and if inlen is 1, the data + * is transferred to the device. + */ + outlen = ioc.outlen; + inlen = ioc.inlen; + + if(outlen) { + ret = verify_area(VERIFY_WRITE, (char *)ioc.ui.fcs.buffer, length); + if (ret) return ret; + } + if(inlen) { + ret = verify_area(VERIFY_READ, (char *) ioc.ui.fcs.buffer, length); + if (ret) return ret; + } + + /* + * Find this host + */ + for( shpnt = scsi_hostlist; shpnt; shpnt = shpnt->next ) { + if( shpnt->hostdata == (unsigned long *)megaCtlrs[adapno] ) { + megacfg = (mega_host_config *)shpnt->hostdata; + break; + } + } + if(shpnt == NULL) return -ENODEV; + +#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,4,0) + scsicmd = (Scsi_Cmnd *)kmalloc(sizeof(Scsi_Cmnd), GFP_KERNEL|GFP_DMA); +#else + scsicmd = (Scsi_Cmnd *)scsi_init_malloc(sizeof(Scsi_Cmnd), + GFP_ATOMIC | GFP_DMA); +#endif + if(scsicmd == NULL) return -ENOMEM; + + memset(scsicmd, 0, sizeof(Scsi_Cmnd)); + scsicmd->host = shpnt; + + if( outlen || inlen ) { +#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,4,0) + pdevp = &pdev; + memcpy(pdevp, megacfg->dev, sizeof(struct pci_dev)); + pdevp->dma_mask = 0xffffffff; +#else + pdevp = NULL; +#endif + kvaddr = dma_alloc_consistent(pdevp, length, &dma_addr); + + if( kvaddr == NULL ) { + printk(KERN_WARNING "megaraid:allocation failed\n"); +#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,4,0) /*0x20400 */ + kfree(scsicmd); +#else + scsi_init_free((char *)scsicmd, sizeof(Scsi_Cmnd)); +#endif + return -ENOMEM; + } + + ioc.ui.fcs.buffer = kvaddr; + + if (inlen) { + /* copyin the user data */ + copy_from_user(kvaddr, (char *)uaddr, length ); + } + } + + scsicmd->cmnd[0] = MEGADEVIOC; + scsicmd->request_buffer = (void *)&ioc; + +#if XENO_KILLED + init_MUTEX_LOCKED(&mimd_ioctl_sem); +#endif + + IO_LOCK; + megaraid_queue(scsicmd, megadev_ioctl_done); + + IO_UNLOCK; +#if XENO_KILLED + down(&mimd_ioctl_sem); +#endif + if( !scsicmd->result && outlen ) { + if (copy_to_user(uaddr, kvaddr, length)) + return -EFAULT; + } + + /* + * copyout the result + */ + uioc = (struct uioctl_t *)arg; + + if( ioc.mbox[0] == MEGA_MBOXCMD_PASSTHRU ) { + put_user( scsicmd->result, &uioc->pthru.scsistatus ); + if (copy_to_user( uioc->pthru.reqsensearea, scsicmd->sense_buffer, + MAX_REQ_SENSE_LEN )); + ret= -EFAULT; + } else { + put_user(1, &uioc->mbox[16]); /* numstatus */ + /* status */ + put_user (scsicmd->result, &uioc->mbox[17]); + } + + if (kvaddr) { + dma_free_consistent(pdevp, length, kvaddr, dma_addr); + } +#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,4,0) /*0x20400 */ + kfree (scsicmd); +#else + scsi_init_free((char *)scsicmd, sizeof(Scsi_Cmnd)); +#endif + + /* restore the user address */ + ioc.ui.fcs.buffer = uaddr; + + return ret; + + case M_RD_IOCTL_CMD: + /* which adapter? */ + adapno = ioc.ui.fcs.adapno; + + /* See comment above: MEGAIOC_QADAPINFO */ + adapno = GETADAP (adapno); + + if (adapno >= numCtlrs) + return (-ENODEV); + + /* save the user address */ + uaddr = ioc.data; + outlen = ioc.outlen; + inlen = ioc.inlen; + + if ((outlen >= IOCTL_MAX_DATALEN) || (inlen >= IOCTL_MAX_DATALEN)) + return (-EINVAL); + + if (outlen) { + ret = verify_area (VERIFY_WRITE, ioc.data, outlen); + if (ret) return ret; + } + if (inlen) { + ret = verify_area (VERIFY_READ, ioc.data, inlen); + if (ret) return ret; + } + + /* + * Find this host + */ + for( shpnt = scsi_hostlist; shpnt; shpnt = shpnt->next ) { + if( shpnt->hostdata == (unsigned long *)megaCtlrs[adapno] ) { + megacfg = (mega_host_config *)shpnt->hostdata; + break; + } + } + if(shpnt == NULL) return -ENODEV; + + /* + * ioctls for deleting logical drives is a special case, so check + * for it first + */ + if( ioc.mbox[0] == FC_DEL_LOGDRV && ioc.mbox[2] == OP_DEL_LOGDRV ) { + + if( !megacfg->support_random_del ) { + printk("megaraid: logdrv delete on non supporting f/w.\n"); + return -EINVAL; + } + + uioc = (struct uioctl_t *)arg; + + ret = mega_del_logdrv(megacfg, ioc.mbox[3]); + + put_user(1, &uioc->mbox[16]); /* numstatus */ + put_user(ret, &uioc->mbox[17]); /* status */ + + /* if deletion failed, let the user know by failing ioctl */ + return ret; + } + +#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,4,0) + scsicmd = (Scsi_Cmnd *)kmalloc(sizeof(Scsi_Cmnd), GFP_KERNEL|GFP_DMA); +#else + scsicmd = (Scsi_Cmnd *)scsi_init_malloc(sizeof(Scsi_Cmnd), + GFP_ATOMIC | GFP_DMA); +#endif + if(scsicmd == NULL) return -ENOMEM; + + memset(scsicmd, 0, sizeof(Scsi_Cmnd)); + scsicmd->host = shpnt; + + if (outlen || inlen) { +#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,4,0) + pdevp = &pdev; + memcpy(pdevp, megacfg->dev, sizeof(struct pci_dev)); + pdevp->dma_mask = 0xffffffff; +#else + pdevp = NULL; +#endif + /* + * Allocate a page of kernel space. + */ + kvaddr = dma_alloc_consistent(pdevp, PAGE_SIZE, &dma_addr); + + if( kvaddr == NULL ) { + printk (KERN_WARNING "megaraid:allocation failed\n"); +#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,4,0) /*0x20400 */ + kfree(scsicmd); +#else + scsi_init_free((char *)scsicmd, sizeof(Scsi_Cmnd)); +#endif + return -ENOMEM; + } + + ioc.data = kvaddr; + + if (inlen) { + if (ioc.mbox[0] == MEGA_MBOXCMD_PASSTHRU) { + /* copyin the user data */ + copy_from_user (kvaddr, uaddr, ioc.pthru.dataxferlen); + } else { + copy_from_user (kvaddr, uaddr, inlen); + } + } + } + + scsicmd->cmnd[0] = MEGADEVIOC; + scsicmd->request_buffer = (void *) &ioc; + +#if XENO_KILLED + init_MUTEX_LOCKED (&mimd_ioctl_sem); +#endif + + IO_LOCK; + megaraid_queue (scsicmd, megadev_ioctl_done); + + IO_UNLOCK; +#if XENO_KILLED + down (&mimd_ioctl_sem); +#endif + + if (!scsicmd->result && outlen) { + if (ioc.mbox[0] == MEGA_MBOXCMD_PASSTHRU) { + if (copy_to_user (uaddr, kvaddr, ioc.pthru.dataxferlen)) + ret = -EFAULT; + } else { + if (copy_to_user (uaddr, kvaddr, outlen)) + ret = -EFAULT; + } + } + + /* + * copyout the result + */ + uioc = (struct uioctl_t *) arg; + + if (ioc.mbox[0] == MEGA_MBOXCMD_PASSTHRU) { + put_user (scsicmd->result, &uioc->pthru.scsistatus); + + /* + * If scsicmd->result is 0x02 (CHECK CONDITION) then copy the + * SCSI sense data into user area + */ + if (copy_to_user( uioc->pthru.reqsensearea, scsicmd->sense_buffer, + MAX_REQ_SENSE_LEN )) + ret = -EFAULT; + + } else { + put_user (1, &uioc->mbox[16]); /* numstatus */ + put_user (scsicmd->result, &uioc->mbox[17]); /* status */ + } + + if (kvaddr) { + dma_free_consistent(pdevp, PAGE_SIZE, kvaddr, dma_addr ); + } + +#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,4,0) + kfree (scsicmd); +#else + scsi_init_free((char *)scsicmd, sizeof(Scsi_Cmnd)); +#endif + + /* restore user pointer */ + ioc.data = uaddr; + + return ret; + + default: + return (-EINVAL); + + }/* Outer switch */ + + return 0; +} + +static void +megadev_ioctl_done(Scsi_Cmnd *sc) +{ +#if XENO_KILLED + up (&mimd_ioctl_sem); +#endif +} + +static mega_scb * +megadev_doioctl (mega_host_config * megacfg, Scsi_Cmnd * sc) +{ + u8 cmd; + struct uioctl_t *ioc = NULL; + mega_mailbox *mbox = NULL; + mega_ioctl_mbox *mboxioc = NULL; + struct mbox_passthru *mboxpthru = NULL; + mega_scb *scb = NULL; + mega_passthru *pthru = NULL; + + if ((scb = mega_allocateSCB (megacfg, sc)) == NULL) { + sc->result = (DID_ERROR << 16); + callDone (sc); + return NULL; + } + + ioc = (struct uioctl_t *) sc->request_buffer; + + memcpy (scb->mboxData, ioc->mbox, sizeof (scb->mboxData)); + + /* The generic mailbox */ + mbox = (mega_mailbox *) ioc->mbox; + + /* + * Get the user command + */ + cmd = ioc->mbox[0]; + + switch (cmd) { + case MEGA_MBOXCMD_PASSTHRU: + /* + * prepare the SCB with information from the user ioctl structure + */ +#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,4,0) + pthru = scb->pthru; +#else + pthru = &scb->pthru; +#endif + memcpy (pthru, &ioc->pthru, sizeof (mega_passthru)); + mboxpthru = (struct mbox_passthru *) scb->mboxData; +#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,4,0) + if (megacfg->flag & BOARD_64BIT) { + /* This is just a sample with one element + * This if executes onlu on 2.4 kernels + */ + mboxpthru->dataxferaddr = scb->dma_passthruhandle64; + scb->sg64List[0].address = + pci_map_single (megacfg->dev, + ioc->data, + 4096, PCI_DMA_BIDIRECTIONAL); + scb->sg64List[0].length = 4096; // TODO: Check this + pthru->dataxferaddr = scb->dma_sghandle64; + pthru->numsgelements = 1; + mboxpthru->cmd = MEGA_MBOXCMD_PASSTHRU64; + } else { + mboxpthru->dataxferaddr = scb->dma_passthruhandle64; + pthru->dataxferaddr = + pci_map_single (megacfg->dev, + ioc->data, + 4096, PCI_DMA_BIDIRECTIONAL); + pthru->numsgelements = 0; + } + +#else + { + mboxpthru->dataxferaddr = virt_to_bus (&scb->pthru); + pthru->dataxferaddr = virt_to_bus (ioc->data); + pthru->numsgelements = 0; + } +#endif + + pthru->reqsenselen = 14; + break; + + default: /* Normal command */ + mboxioc = (mega_ioctl_mbox *) scb->mboxData; + + if (ioc->ui.fcs.opcode == M_RD_IOCTL_CMD_NEW) { + scb->buff_ptr = ioc->ui.fcs.buffer; + scb->iDataSize = ioc->ui.fcs.length; + } else { + scb->buff_ptr = ioc->data; + scb->iDataSize = 4096; // TODO:check it + } + + set_mbox_xfer_addr (megacfg, scb, mboxioc, FROMTO_DEVICE); + mboxioc->numsgelements = 0; + break; + } + + return scb; +} + +static int +megadev_close (struct inode *inode, struct file *filep) +{ +#ifdef MODULE + MOD_DEC_USE_COUNT; +#endif + return 0; +} + + +static int +mega_support_ext_cdb(mega_host_config *this_hba) +{ + mega_mailbox *mboxpnt; + unsigned char mbox[16]; + int ret; + + mboxpnt = (mega_mailbox *) mbox; + + memset(mbox, 0, sizeof (mbox)); + /* + * issue command to find out if controller supports extended CDBs. + */ + mbox[0] = 0xA4; + mbox[2] = 0x16; + + ret = megaIssueCmd(this_hba, mbox, NULL, 0); + + return !ret; +} + + +/* + * Find out if this controller supports random deletion and addition of + * logical drives + */ +static int +mega_support_random_del(mega_host_config *this_hba) +{ +#if XENO_KILLED + mega_mailbox *mboxpnt; + unsigned char mbox[16]; + int ret; + + mboxpnt = (mega_mailbox *)mbox; + + memset(mbox, 0, sizeof(mbox)); + + /* + * issue command + */ + mbox[0] = FC_DEL_LOGDRV; + mbox[2] = OP_SUP_DEL_LOGDRV; + + ret = megaIssueCmd(this_hba, mbox, NULL, 0); + + return !ret; +#endif + return 0; // no support for random deletions +} + +static int +mega_del_logdrv(mega_host_config *this_hba, int logdrv) +{ + return -ENOSYS; +#if XENO_KILLED + int rval; + IO_LOCK_T; + DECLARE_WAIT_QUEUE_HEAD(wq); + mega_scb *scbp; + + /* + * Stop sending commands to the controller, queue them internally. + * When deletion is complete, ISR will flush the queue. + */ + IO_LOCK; + this_hba->quiescent = 1; + IO_UNLOCK; + + while( this_hba->qPcnt ) { + sleep_on_timeout( &wq, 1*HZ ); /* sleep for 1s */ + } + rval = mega_do_del_logdrv(this_hba, logdrv); + + IO_LOCK; + /* + * Attach the internal queue to the pending queue + */ + if( this_hba->qPendingH == NULL ) { + /* + * If pending queue head is null, make internal queue as + * pending queue + */ + this_hba->qPendingH = this_hba->int_qh; + this_hba->qPendingT = this_hba->int_qt; + this_hba->qPcnt = this_hba->int_qlen; + } + else { + /* + * Append pending queue to internal queue + */ + if( this_hba->int_qt ) { + this_hba->int_qt->next = this_hba->qPendingH; + + this_hba->qPendingH = this_hba->int_qh; + this_hba->qPcnt += this_hba->int_qlen; + } + } + + this_hba->int_qh = this_hba->int_qt = NULL; + this_hba->int_qlen = 0; + + /* + * If delete operation was successful, add 0x80 to the logical drive + * ids for commands in the pending queue. + */ + if( this_hba->read_ldidmap) { + for( scbp = this_hba->qPendingH; scbp; scbp = scbp->next ) { +#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,4,0) + if( scbp->pthru->logdrv < 0x80 ) + scbp->pthru->logdrv += 0x80; +#else + if( scbp->pthru.logdrv < 0x80 ) + scbp->pthru.logdrv += 0x80; +#endif + } + } + this_hba->quiescent = 0; + + IO_UNLOCK; + + return rval; +#endif +} + + +static int +mega_do_del_logdrv(mega_host_config *this_hba, int logdrv) +{ + mega_mailbox *mboxpnt; + unsigned char mbox[16]; + int rval; + + mboxpnt = (mega_mailbox *)mbox; + + memset(mbox, 0, sizeof(mbox)); + + mbox[0] = FC_DEL_LOGDRV; + mbox[2] = OP_DEL_LOGDRV; + mbox[3] = logdrv; + + rval = megaIssueCmd(this_hba, mbox, NULL, 0); + + /* log this event */ + if( rval != 0 ) { + printk("megaraid: Attempt to delete logical drive %d failed.", + logdrv); + return rval; + } + + printk("megaraid: logical drive %d deleted.\n", logdrv); + + /* + * After deleting first logical drive, the logical drives must be + * addressed by adding 0x80 to the logical drive id. + */ + this_hba->read_ldidmap = 1; + + return rval; +} + +#if LINUX_VERSION_CODE < KERNEL_VERSION(2,4,0) +void * +dma_alloc_consistent(void *dev, size_t size, dma_addr_t *dma_addr) +{ + void *_tv; + int npages; + int order = 0; + + /* + * How many pages application needs + */ + npages = size / PAGE_SIZE; + + /* Do we need one more page */ + if(size % PAGE_SIZE) + npages++; + + order = mega_get_order(npages); + + _tv = (void *)__get_free_pages(GFP_DMA, order); + + if( _tv != NULL ) { + memset(_tv, 0, size); + *(dma_addr) = virt_to_bus(_tv); + } + + return _tv; +} + +/* + * int mega_get_order(int) + * + * returns the order to be used as 2nd argument to __get_free_pages() - which + * return pages equal to pow(2, order) - AM + */ +int +mega_get_order(int n) +{ + int i = 0; + + while( pow_2(i++) < n ) + ; /* null statement */ + + return i-1; +} + +/* + * int pow_2(int) + * + * calculates pow(2, i) + */ +int +pow_2(int i) +{ + unsigned int v = 1; + + while(i--) + v <<= 1; + + return v; +} + +void +dma_free_consistent(void *dev, size_t size, void *vaddr, dma_addr_t dma_addr) +{ + int npages; + int order = 0; + + npages = size / PAGE_SIZE; + + if(size % PAGE_SIZE) + npages++; + + if (npages == 1) + order = 0; + else if (npages == 2) + order = 1; + else if (npages <= 4) + order = 2; + else + order = 3; + + free_pages((unsigned long)vaddr, order); + +} +#endif + +#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,4,0) +static +#endif /* LINUX VERSION 2.4.XX */ +#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,4,0) || defined(MODULE) +Scsi_Host_Template driver_template = MEGARAID; + +#include "scsi_module.c.inc" +#endif /* LINUX VERSION 2.4.XX || MODULE */ + +/* vi: set ts=4: */ diff --git a/xen/drivers/scsi/megaraid.h b/xen/drivers/scsi/megaraid.h new file mode 100644 index 0000000000..a84a7ac900 --- /dev/null +++ b/xen/drivers/scsi/megaraid.h @@ -0,0 +1,1040 @@ +#ifndef __MEGARAID_H__ +#define __MEGARAID_H__ + +#include + +/* + * For state flag. Do not use LSB(8 bits) which are + * reserved for storing info about channels. + */ +#define IN_ISR 0x80000000L +#define IN_ABORT 0x40000000L +#define IN_RESET 0x20000000L +#define IN_QUEUE 0x10000000L +#define BOARD_QUARTZ 0x08000000L +#define BOARD_40LD 0x04000000L +#define BOARD_64BIT 0x02000000L + +#define SCB_FREE 0x0 +#define SCB_ACTIVE 0x1 +#define SCB_WAITQ 0x2 +#define SCB_ISSUED 0x3 +#define SCB_COMPLETE 0x4 +#define SCB_ABORTED 0x5 +#define SCB_RESET 0x6 + +#define M_RD_CRLFSTR "\n" +#define M_RD_IOCTL_CMD 0x80 +#define M_RD_IOCTL_CMD_NEW 0x81 +#define M_RD_DRIVER_IOCTL_INTERFACE 0x82 + +#define MEGARAID_VERSION "v1.18d (Release Date: Wed Aug 7 18:51:51 EDT 2002)\n" + +#define MEGARAID_IOCTL_VERSION 114 + +/* Methods */ +#define GET_DRIVER_INFO 0x1 + +#define MEGA_CMD_TIMEOUT 10 + +/* Feel free to fiddle with these.. max values are: + SGLIST 0..26 + COMMANDS 0..253 + CMDPERLUN 0..63 +*/ + +#define MAX_SGLIST 0x1A +#define MAX_COMMANDS 127 +#define MAX_CMD_PER_LUN 63 +#define MAX_FIRMWARE_STATUS 46 + +#define MAX_LOGICAL_DRIVES 8 +#define MAX_CHANNEL 5 +#define MAX_TARGET 15 +#define MAX_PHYSICAL_DRIVES MAX_CHANNEL*MAX_TARGET + +#define INQUIRY_DATA_SIZE 0x24 +#define MAX_CDB_LEN 0x0A +#define MAX_REQ_SENSE_LEN 0x20 + +#define INTR_VALID 0x40 + +/* Direction Macros for MBOX Data direction */ +#define TO_DEVICE 0x0 +#define FROM_DEVICE 0x1 +#define FROMTO_DEVICE 0x2 + +/* Mailbox commands */ +#define MEGA_MBOXCMD_LREAD 0x01 +#define MEGA_MBOXCMD_LWRITE 0x02 +#define MEGA_MBOXCMD_LREAD64 0xA7 +#define MEGA_MBOXCMD_LWRITE64 0xA8 +#define MEGA_MBOXCMD_PASSTHRU 0x03 +#define MEGA_MBOXCMD_EXTPASSTHRU 0xE3 +#define MEGA_MBOXCMD_PASSTHRU64 0xC3 +#define MEGA_MBOXCMD_ADAPTERINQ 0x05 + + +/* Offsets into Mailbox */ +#define COMMAND_PORT 0x00 +#define COMMAND_ID_PORT 0x01 +#define SG_LIST_PORT0 0x08 +#define SG_LIST_PORT1 0x09 +#define SG_LIST_PORT2 0x0a +#define SG_LIST_PORT3 0x0b +#define SG_ELEMENT_PORT 0x0d +#define NO_FIRED_PORT 0x0f + +/* I/O Port offsets */ +#define I_CMD_PORT 0x00 +#define I_ACK_PORT 0x00 +#define I_TOGGLE_PORT 0x01 +#define INTR_PORT 0x0a + +#define MAILBOX_SIZE (sizeof(mega_mailbox)-16) +#define MBOX_BUSY_PORT 0x00 +#define MBOX_PORT0 0x04 +#define MBOX_PORT1 0x05 +#define MBOX_PORT2 0x06 +#define MBOX_PORT3 0x07 +#define ENABLE_MBOX_REGION 0x0B + +/* I/O Port Values */ +#define ISSUE_BYTE 0x10 +#define ACK_BYTE 0x08 +#define ENABLE_INTR_BYTE 0xc0 +#define DISABLE_INTR_BYTE 0x00 +#define VALID_INTR_BYTE 0x40 +#define MBOX_BUSY_BYTE 0x10 +#define ENABLE_MBOX_BYTE 0x00 + +/* Setup some port macros here */ +#define WRITE_MAILBOX(base,offset,value) *(base+offset)=value +#define READ_MAILBOX(base,offset) *(base+offset) + +#define WRITE_PORT(base,offset,value) outb_p(value,base+offset) +#define READ_PORT(base,offset) inb_p(base+offset) + +#define ISSUE_COMMAND(base) WRITE_PORT(base,I_CMD_PORT,ISSUE_BYTE) +#define CLEAR_INTR(base) WRITE_PORT(base,I_ACK_PORT,ACK_BYTE) +#define ENABLE_INTR(base) WRITE_PORT(base,I_TOGGLE_PORT,ENABLE_INTR_BYTE) +#define DISABLE_INTR(base) WRITE_PORT(base,I_TOGGLE_PORT,DISABLE_INTR_BYTE) + +#ifndef PCI_VENDOR_ID_LSI_LOGIC +#define PCI_VENDOR_ID_LSI_LOGIC 0x1000 +#endif + +/* Define AMI's PCI codes */ +#ifndef PCI_VENDOR_ID_AMI +#define PCI_VENDOR_ID_AMI 0x101E +#endif + +#ifndef PCI_DEVICE_ID_AMI_MEGARAID +#define PCI_DEVICE_ID_AMI_MEGARAID 0x9010 +#endif + +#ifndef PCI_DEVICE_ID_AMI_MEGARAID2 +#define PCI_DEVICE_ID_AMI_MEGARAID2 0x9060 +#endif + +#ifndef PCI_DEVICE_ID_AMI_MEGARAID3 +#define PCI_DEVICE_ID_AMI_MEGARAID3 0x1960 +#endif + +#define PCI_VENDOR_ID_DISCOVERY 0x1028 +#define PCI_DEVICE_ID_DISCOVERY 0x000E + +#define PCI_VENDOR_ID_PERC4_DI_YSTONE 0x1028 +#define PCI_DEVICE_ID_PERC4_DI_YSTONE 0x000F + +#define PCI_VENDOR_ID_PERC4_QC_VERDE 0x1000 +#define PCI_DEVICE_ID_PERC4_QC_VERDE 0x0407 + +/* Special Adapter Commands */ +#define FW_FIRE_WRITE 0x2C +#define FW_FIRE_FLASH 0x2D + +#define FC_NEW_CONFIG 0xA1 +#define DCMD_FC_CMD 0xA1 +#define DCMD_FC_PROCEED 0x02 +#define DCMD_DELETE_LOGDRV 0x03 +#define DCMD_FC_READ_NVRAM_CONFIG 0x04 +#define DCMD_FC_READ_NVRAM_CONFIG_64 0xC0 +#define DCMD_FC_READ_FINAL_CONFIG 0x05 +#define DCMD_GET_DISK_CONFIG 0x06 +#define DCMD_GET_DISK_CONFIG_64 0xC2 +#define DCMD_CHANGE_LDNO 0x07 +#define DCMD_COMPACT_CONFIG 0x08 +#define DCMD_DELETE_DRIVEGROUP 0x09 +#define DCMD_GET_LOOPID_INFO 0x0A +#define DCMD_CHANGE_LOOPID 0x0B +#define DCMD_GET_NUM_SCSI_CHANS 0x0C +#define DCMD_WRITE_CONFIG 0x0D +#define DCMD_WRITE_CONFIG_64 0xC1 + +#define NC_SUBOP_PRODUCT_INFO 0x0E +#define NC_SUBOP_ENQUIRY3 0x0F +#define ENQ3_GET_SOLICITED_NOTIFY_ONLY 0x01 +#define ENQ3_GET_SOLICITED_FULL 0x02 +#define ENQ3_GET_UNSOLICITED 0x03 + +#define PCI_CONF_BASE_ADDR_OFFSET 0x10 +#define PCI_CONF_IRQ_OFFSET 0x3c +#define PCI_CONF_AMISIG 0xa0 +#define PCI_CONF_AMISIG64 0xa4 + +/* Sub-System Vendor ID sorted on alphabetical order*/ +#define LSI_SUBSYS_ID 0x1000 +#define AMI_SUBSYS_ID 0x101E +#define DELL_SUBSYS_ID 0x1028 +#define HP_SUBSYS_ID 0x103C + +#define AMI_SIGNATURE 0x3344 +#define AMI_SIGNATURE_471 0xCCCC +#define AMI_64BIT_SIGNATURE 0x0299 + +#if LINUX_VERSION_CODE < KERNEL_VERSION(2,1,0) /*0x20100 */ +#define MEGARAID \ + { NULL, /* Next */\ + NULL, /* Usage Count Pointer */\ + NULL, /* proc Directory Entry */\ + megaraid_proc_info, /* proc Info Function */\ + "MegaRAID", /* Driver Name */\ + megaraid_detect, /* Detect Host Adapter */\ + megaraid_release, /* Release Host Adapter */\ + megaraid_info, /* Driver Info Function */\ + megaraid_command, /* Command Function */\ + megaraid_queue, /* Queue Command Function */\ + megaraid_abort, /* Abort Command Function */\ + megaraid_reset, /* Reset Command Function */\ + NULL, /* Slave Attach Function */\ + megaraid_biosparam, /* Disk BIOS Parameters */\ + MAX_COMMANDS, /* # of cmds that can be\ + outstanding at any time */\ + 7, /* HBA Target ID */\ + MAX_SGLIST, /* Scatter/Gather Table Size */\ + MAX_CMD_PER_LUN, /* SCSI Commands per LUN */\ + 0, /* Present */\ + 0, /* Default Unchecked ISA DMA */\ + ENABLE_CLUSTERING } /* Enable Clustering */ +#else +#define MEGARAID \ + {\ + name: "MegaRAID", /* Driver Name */\ + detect: megaraid_detect, /* Detect Host Adapter */\ + release: megaraid_release, /* Release Host Adapter */\ + info: megaraid_info, /* Driver Info Function */\ + command: NULL, /* XEN KILLED */\ + queuecommand: megaraid_queue, /* Queue Command Function */\ + abort: megaraid_abort, /* Abort Command Function */\ + reset: megaraid_reset, /* Reset Command Function */\ + bios_param: megaraid_biosparam, /* Disk BIOS Parameters */\ + can_queue: MAX_COMMANDS, /* Can Queue */\ + this_id: 7, /* HBA Target ID */\ + sg_tablesize: MAX_SGLIST, /* Scatter/Gather Table Size */\ + cmd_per_lun: MAX_CMD_PER_LUN, /* SCSI Commands per LUN */\ + present: 0, /* Present */\ + unchecked_isa_dma: 0, /* Default Unchecked ISA DMA */\ + use_clustering: ENABLE_CLUSTERING, /* Enable Clustering */\ + } +#endif +// XENO: REMOVED command: megaraid_command, /* Command Function */ +// XENO: REMOVED proc_info: megaraid_proc_info, /* /proc driver info */ +// XENO: REMOVED highmem_io: 1, /* enable HIGHMEM I/O */ + + +/*********************************************************************** + * Structure Declarations for the Firmware supporting 40 Logical Drives + * and 256 Physical Drives. + ***********************************************************************/ + +#define FC_MAX_LOGICAL_DRIVES 40 +#define FC_MAX_LOG_DEVICES FC_MAX_LOGICAL_DRIVES +#define FC_MAX_SPAN_DEPTH 8 +#define FC_MAX_ROW_SIZE 32 + +#define FC_MAX_CHANNELS 16 +#define FC_MAX_TARGETS_PER_CHANNEL 16 +#define FC_MAX_PHYSICAL_DEVICES 256 + +/******************************************** + * PRODUCT_INFO + ********************************************/ + +#define SIG_40LOG_32STR_8SPN 0x00282008 + +/* + * Utilities declare this strcture size as 1024 bytes. So more fields can + * be added in future. + */ + +struct MRaidProductInfo { + u32 DataSize; /* current size in bytes (not including resvd) */ + u32 ConfigSignature; + /* Current value is 0x00282008 + * 0x28=MAX_LOGICAL_DRIVES, + * 0x20=Number of stripes and + * 0x08=Number of spans */ + u8 FwVer[16]; /* printable ASCI string */ + u8 BiosVer[16]; /* printable ASCI string */ + u8 ProductName[80]; /* printable ASCI string */ + + u8 MaxConcCmds; /* Max. concurrent commands supported */ + u8 SCSIChanPresent; /* Number of SCSI Channels detected */ + u8 FCLoopPresent; /* Number of Fibre Loops detected */ + u8 memType; /* EDO, FPM, SDRAM etc */ + + u32 signature; + u16 DramSize; /* In terms of MB */ + u16 subSystemID; + + u16 subSystemVendorID; + u8 numNotifyCounters; + u8 pad1k[889]; /* 135 + 889 resvd = 1024 total size */ +} __attribute__ ((packed)); +typedef struct MRaidProductInfo megaRaidProductInfo; + +/******************************************** + * Standard ENQUIRY + ********************************************/ +struct FC_ADP_INFO { + u8 MaxConcCmds; /* Max. concurrent commands supported. */ + u8 RbldRate; /* Rebuild Rate. Varies from 0%-100% */ + u8 MaxTargPerChan; /* Max. Targets supported per chan. */ + u8 ChanPresent; /* No. of Chans present on this adapter. */ + u8 FwVer[4]; /* Firmware version. */ + u16 AgeOfFlash; /* No. of times FW has been downloaded. */ + u8 ChipSetValue; /* Contents of 0xC0000832 */ + u8 DramSize; /* In terms of MB */ + u8 CacheFlushInterval; /* In terms of Seconds */ + u8 BiosVersion[4]; + u8 BoardType; + u8 sense_alert; + u8 write_config_count; /* Increase with evry configuration change */ + u8 drive_inserted_count;/* Increase with every drive inserted */ + u8 inserted_drive; /* Channel: Id of inserted drive */ + u8 battery_status; + /* + BIT 0 : battery module missing + BIT 1 : VBAD + BIT 2 : temp high + BIT 3 : battery pack missing + BIT 4,5 : 00 - charge complete + 01 - fast charge in prog + 10 - fast charge fail + 11 - undefined + BIt 6 : counter > 1000 + Bit 7 : undefined + */ + u8 dec_fault_bus_info; /* was resvd */ +} __attribute__ ((packed)); + +struct FC_LDRV_INFO { + u8 NumLDrv; /* No. of Log. Drvs configured. */ + u8 recon_state[FC_MAX_LOGICAL_DRIVES / 8]; + /* bit field for State of reconstruct */ + u16 LDrvOpStatus[FC_MAX_LOGICAL_DRIVES / 8]; + /* bit field Status of Long Operations. */ + + u32 LDrvSize[FC_MAX_LOGICAL_DRIVES]; /* Size of each log. Drv. */ + u8 LDrvProp[FC_MAX_LOGICAL_DRIVES]; + u8 LDrvState[FC_MAX_LOGICAL_DRIVES]; /* State of Logical Drives. */ +} __attribute__ ((packed)); + +#define PREVSTAT_MASK 0xf0 +#define CURRSTAT_MASK 0x0f + +struct FC_PDRV_INFO { + u8 PDrvState[FC_MAX_PHYSICAL_DEVICES]; /* State of Phys Drvs. */ +} __attribute__ ((packed)); + +struct FC_AdapterInq { + struct FC_ADP_INFO AdpInfo; + struct FC_LDRV_INFO LogdrvInfo; + struct FC_PDRV_INFO PhysdrvInfo; +} __attribute__ ((packed)); + +typedef struct FC_AdapterInq mega_RAIDINQ_FC; + +/******************************************** + * NOTIFICATION + ********************************************/ + +#define MAX_NOTIFY_SIZE 0x80 +#define CUR_NOTIFY_SIZE sizeof(struct MegaRAID_Notify) + +/* + * Utilities declare this strcture size as ?? bytes. So more fields can + * be added in future. + */ +struct MegaRAID_Notify { + u32 globalCounter; /* Any change increments this counter */ + + u8 paramCounter; /* Indicates any params changed */ + u8 paramId; /* Param modified - defined below */ + u16 paramVal; /* New val of last param modified */ + + u8 writeConfigCounter; /* write config occurred */ + u8 writeConfigRsvd[3]; + + u8 ldrvOpCounter; /* Indicates ldrv op started/completed */ + u8 ldrvOpId; /* ldrv num */ + u8 ldrvOpCmd; /* ldrv operation - defined below */ + u8 ldrvOpStatus; /* status of the operation */ + + u8 ldrvStateCounter; /* Indicates change of ldrv state */ + u8 ldrvStateId; /* ldrv num */ + u8 ldrvStateNew; /* New state */ + u8 ldrvStateOld; /* old state */ + + u8 pdrvStateCounter; /* Indicates change of ldrv state */ + u8 pdrvStateId; /* pdrv id */ + u8 pdrvStateNew; /* New state */ + u8 pdrvStateOld; /* old state */ + + u8 pdrvFmtCounter; /* Indicates pdrv format started/over */ + u8 pdrvFmtId; /* pdrv id */ + u8 pdrvFmtVal; /* format started/over */ + u8 pdrvFmtRsvd; + + u8 targXferCounter; /* Indicates SCSI-2 Xfer rate change */ + u8 targXferId; /* pdrv Id */ + u8 targXferVal; /* new Xfer params of last pdrv */ + u8 targXferRsvd; + + u8 fcLoopIdChgCounter; /* Indicates loopid changed */ + u8 fcLoopIdPdrvId; /* pdrv id */ + u8 fcLoopId0; /* loopid on fc loop 0 */ + u8 fcLoopId1; /* loopid on fc loop 1 */ + + u8 fcLoopStateCounter; /* Indicates loop state changed */ + u8 fcLoopState0; /* state of fc loop 0 */ + u8 fcLoopState1; /* state of fc loop 1 */ + u8 fcLoopStateRsvd; +} __attribute__ ((packed)); + +/******************************************** + * PARAM IDs in Notify struct + ********************************************/ +#define PARAM_RBLD_RATE 0x01 + /*-------------------------------------- + * Param val = + * byte 0: new rbld rate + *--------------------------------------*/ +#define PARAM_CACHE_FLUSH_INTERVAL 0x02 + /*-------------------------------------- + * Param val = + * byte 0: new cache flush interval + *--------------------------------------*/ +#define PARAM_SENSE_ALERT 0x03 + /*-------------------------------------- + * Param val = + * byte 0: last pdrv id causing chkcond + *--------------------------------------*/ +#define PARAM_DRIVE_INSERTED 0x04 + /*-------------------------------------- + * Param val = + * byte 0: last pdrv id inserted + *--------------------------------------*/ +#define PARAM_BATTERY_STATUS 0x05 + /*-------------------------------------- + * Param val = + * byte 0: battery status + *--------------------------------------*/ + +/******************************************** + * Ldrv operation cmd in Notify struct + ********************************************/ +#define LDRV_CMD_CHKCONSISTANCY 0x01 +#define LDRV_CMD_INITIALIZE 0x02 +#define LDRV_CMD_RECONSTRUCTION 0x03 + +/******************************************** + * Ldrv operation status in Notify struct + ********************************************/ +#define LDRV_OP_SUCCESS 0x00 +#define LDRV_OP_FAILED 0x01 +#define LDRV_OP_ABORTED 0x02 +#define LDRV_OP_CORRECTED 0x03 +#define LDRV_OP_STARTED 0x04 + +/******************************************** + * Raid Logical drive states. + ********************************************/ +#define RDRV_OFFLINE 0 +#define RDRV_DEGRADED 1 +#define RDRV_OPTIMAL 2 +#define RDRV_DELETED 3 + +/******************************************* + * Physical drive states. + *******************************************/ +#define PDRV_UNCNF 0 +#define PDRV_ONLINE 3 +#define PDRV_FAILED 4 +#define PDRV_RBLD 5 + +/******************************************* + * Formal val in Notify struct + *******************************************/ +#define PDRV_FMT_START 0x01 +#define PDRV_FMT_OVER 0x02 + +/******************************************** + * FC Loop State in Notify Struct + ********************************************/ +#define ENQ_FCLOOP_FAILED 0 +#define ENQ_FCLOOP_ACTIVE 1 +#define ENQ_FCLOOP_TRANSIENT 2 + +#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,4,0) +#define M_RD_DMA_TYPE_NONE 0xFFFF +#define M_RD_PTHRU_WITH_BULK_DATA 0x0001 +#define M_RD_PTHRU_WITH_SGLIST 0x0002 +#define M_RD_BULK_DATA_ONLY 0x0004 +#define M_RD_SGLIST_ONLY 0x0008 +#define M_RD_EPTHRU_WITH_BULK_DATA 0x0010 +#endif +/******************************************** + * ENQUIRY3 + ********************************************/ +/* + * Utilities declare this strcture size as 1024 bytes. So more fields can + * be added in future. + */ +struct MegaRAID_Enquiry3 { + u32 dataSize; /* current size in bytes (not including resvd) */ + + struct MegaRAID_Notify notify; + + u8 notifyRsvd[MAX_NOTIFY_SIZE - CUR_NOTIFY_SIZE]; + + u8 rbldRate; /* Rebuild rate (0% - 100%) */ + u8 cacheFlushInterval; /* In terms of Seconds */ + u8 senseAlert; + u8 driveInsertedCount; /* drive insertion count */ + + u8 batteryStatus; + u8 numLDrv; /* No. of Log Drives configured */ + u8 reconState[FC_MAX_LOGICAL_DRIVES / 8]; /* State of reconstruct */ + u16 lDrvOpStatus[FC_MAX_LOGICAL_DRIVES / 8]; /* log. Drv Status */ + + u32 lDrvSize[FC_MAX_LOGICAL_DRIVES]; /* Size of each log. Drv */ + u8 lDrvProp[FC_MAX_LOGICAL_DRIVES]; + u8 lDrvState[FC_MAX_LOGICAL_DRIVES]; /* State of Logical Drives */ + u8 pDrvState[FC_MAX_PHYSICAL_DEVICES]; /* State of Phys. Drvs. */ + u16 physDrvFormat[FC_MAX_PHYSICAL_DEVICES / 16]; + + u8 targXfer[80]; /* phys device transfer rate */ + u8 pad1k[263]; /* 761 + 263reserved = 1024 bytes total size */ +} __attribute__ ((packed)); +typedef struct MegaRAID_Enquiry3 mega_Enquiry3; + +/* Structures */ +typedef struct _mega_ADP_INFO { + u8 MaxConcCmds; + u8 RbldRate; + u8 MaxTargPerChan; + u8 ChanPresent; + u8 FwVer[4]; + u16 AgeOfFlash; + u8 ChipSetValue; + u8 DramSize; + u8 CacheFlushInterval; + u8 BiosVer[4]; + u8 resvd[7]; +} mega_ADP_INFO; + +typedef struct _mega_LDRV_INFO { + u8 NumLDrv; + u8 resvd[3]; + u32 LDrvSize[MAX_LOGICAL_DRIVES]; + u8 LDrvProp[MAX_LOGICAL_DRIVES]; + u8 LDrvState[MAX_LOGICAL_DRIVES]; +} mega_LDRV_INFO; + +typedef struct _mega_PDRV_INFO { + u8 PDrvState[MAX_PHYSICAL_DRIVES]; + u8 resvd; +} mega_PDRV_INFO; + +/* RAID inquiry: Mailbox command 0x5*/ +typedef struct _mega_RAIDINQ { + mega_ADP_INFO AdpInfo; + mega_LDRV_INFO LogdrvInfo; + mega_PDRV_INFO PhysdrvInfo; +} mega_RAIDINQ; + +/* Passthrough command: Mailbox command 0x3*/ +typedef struct mega_passthru { + u8 timeout:3; /* 0=6sec/1=60sec/2=10min/3=3hrs */ + u8 ars:1; + u8 reserved:3; + u8 islogical:1; + u8 logdrv; /* if islogical == 1 */ + u8 channel; /* if islogical == 0 */ + u8 target; /* if islogical == 0 */ + u8 queuetag; /* unused */ + u8 queueaction; /* unused */ + u8 cdb[MAX_CDB_LEN]; + u8 cdblen; + u8 reqsenselen; + u8 reqsensearea[MAX_REQ_SENSE_LEN]; + u8 numsgelements; + u8 scsistatus; + u32 dataxferaddr; + u32 dataxferlen; +} mega_passthru; + +/* + * Extended passthru: support CDB > 10 bytes + */ +typedef struct { + u8 timeout:3; /* 0=6sec/1=60sec/2=10min/3=3hrs */ + u8 ars:1; + u8 rsvd1:1; + u8 cd_rom:1; + u8 rsvd2:1; + u8 islogical:1; + + u8 logdrv; /* if islogical == 1 */ + u8 channel; /* if islogical == 0 */ + u8 target; /* if islogical == 0 */ + + u8 queuetag; /* unused */ + u8 queueaction; /* unused */ + + u8 cdblen; + u8 rsvd3; + u8 cdb[16]; + + u8 numsgelements; + u8 status; + u8 reqsenselen; + u8 reqsensearea[MAX_REQ_SENSE_LEN]; + u8 rsvd4; + + u32 dataxferaddr; + u32 dataxferlen; +}mega_ext_passthru; + +struct _mega_mailbox { + /* 0x0 */ u8 cmd; + /* 0x1 */ u8 cmdid; + /* 0x2 */ u16 numsectors; + /* 0x4 */ u32 lba; + /* 0x8 */ u32 xferaddr; + /* 0xC */ u8 logdrv; + /* 0xD */ u8 numsgelements; + /* 0xE */ u8 resvd; + /* 0xF */ u8 busy; + /* 0x10 */ u8 numstatus; + /* 0x11 */ u8 status; + /* 0x12 */ u8 completed[46]; + volatile u8 mraid_poll; + volatile u8 mraid_ack; + u8 pad[16]; /* for alignment purposes */ +} __attribute__ ((packed)); +typedef struct _mega_mailbox mega_mailbox; + +typedef struct { + u32 xferSegment_lo; + u32 xferSegment_hi; + mega_mailbox mailbox; +} mega_mailbox64; + +typedef struct _mega_ioctl_mbox { + /* 0x0 */ u8 cmd; + /* 0x1 */ u8 cmdid; + /* 0x2 */ u8 channel; + /* 0x3 */ u8 param; + /* 0x4 */ u8 pad[4]; + /* 0x8 */ u32 xferaddr; + /* 0xC */ u8 logdrv; + /* 0xD */ u8 numsgelements; + /* 0xE */ u8 resvd; + /* 0xF */ u8 busy; + /* 0x10 */ u8 numstatus; + /* 0x11 */ u8 status; + /* 0x12 */ u8 completed[46]; + u8 mraid_poll; + u8 mraid_ack; + u8 malign[16]; +} mega_ioctl_mbox; + +typedef struct _mega_64sglist32 { + u64 address; + u32 length; +} __attribute__ ((packed)) mega_64sglist; + +typedef struct _mega_sglist { + u32 address; + u32 length; +} mega_sglist; + +/* Queued command data */ +typedef struct _mega_scb mega_scb; + +struct _mega_scb { + int idx; + u32 state; + u32 isrcount; + u8 mboxData[16]; +#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,4,0) + u32 dma_type; + dma_addr_t dma_h_bulkdata; /*Dma handle for bulk data transfter */ + u32 dma_direction; /*Dma direction */ + dma_addr_t dma_h_sgdata; /*Dma handle for the sglist structure */ + dma_addr_t dma_h_sglist[MAX_SGLIST]; /*Dma handle for all SGL elements */ + u8 sglist_count; + dma_addr_t dma_sghandle64; + dma_addr_t dma_passthruhandle64; + dma_addr_t dma_ext_passthruhandle64; + dma_addr_t dma_bounce_buffer; + u8 *bounce_buffer; +#endif + +#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,4,0) + mega_passthru *pthru; + mega_ext_passthru *epthru; +#else + mega_passthru pthru; + mega_ext_passthru epthru; +#endif + + Scsi_Cmnd *SCpnt; + mega_sglist *sgList; + mega_64sglist *sg64List; +#if 0 + struct semaphore ioctl_sem; +#endif + void *buff_ptr; + u32 iDataSize; + mega_scb *next; +}; + +/* internal locking by the queue manipulting routines */ +#define INTERNAL_LOCK 0 +/* external locking by the queue manipulting routines */ +#define EXTERNAL_LOCK 1 +#define NO_LOCK 2 +#define INTR_ENB 0 /* do not disable interrupt while manipulating */ +#define INTR_DIS 1 /* disable interrupt while manipulating */ + +#define NVIRT_CHAN 4 /* # of virtual channels to represent 60 logical + drives */ + +/* Per-controller data */ +typedef struct _mega_host_config { + u8 numldrv; + u32 flag; + +#ifdef __LP64__ + u64 base; +#else + u32 base; +#endif + +#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,4,0) + dma_addr_t dma_handle64, adjdmahandle64; + struct pci_dev *dev; +#endif + + mega_scb *qFreeH; + mega_scb *qFreeT; + spinlock_t lock_free; + + mega_scb *qPendingH; + mega_scb *qPendingT; + spinlock_t lock_pend; + + Scsi_Cmnd *qCompletedH; + Scsi_Cmnd *qCompletedT; + spinlock_t lock_scsicmd; + + u32 qFcnt; + u32 qPcnt; + u32 qCcnt; + + unsigned long nReads[FC_MAX_LOGICAL_DRIVES]; + unsigned long nReadBlocks[FC_MAX_LOGICAL_DRIVES]; + unsigned long nWrites[FC_MAX_LOGICAL_DRIVES]; + unsigned long nWriteBlocks[FC_MAX_LOGICAL_DRIVES]; + unsigned long nInterrupts; + /* Host adapter parameters */ + u8 fwVer[7]; + u8 biosVer[7]; + + struct Scsi_Host *host; + + volatile mega_mailbox64 *mbox64; /* ptr to beginning of 64-bit mailbox */ + volatile mega_mailbox *mbox; /* ptr to beginning of standard mailbox */ + +#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,4,0) +/* ptr to beginning of standard mailbox */ + volatile mega_mailbox64 *mailbox64ptr; +#else + volatile mega_mailbox64 mailbox64; +#endif + + volatile u8 mega_buffer[2 * 1024L]; + volatile megaRaidProductInfo productInfo; + + u8 max_cmds; + mega_scb scbList[MAX_COMMANDS]; + +#if XENO_KILLED +#define PROCBUFSIZE 4096 + char procbuf[PROCBUFSIZE]; + int procidx; + struct proc_dir_entry *controller_proc_dir_entry; + struct proc_dir_entry *proc_read, *proc_stat, *proc_status, *proc_mbox; +#endif + + int support_ext_cdb; + + u8 boot_ldrv_enabled; /* boot from logical drive */ + u8 boot_ldrv; /* boot logical drive */ + u8 boot_pdrv_enabled; /* boot from physical drive */ + u8 boot_pdrv_ch; /* boot physical drive channel */ + u8 boot_pdrv_tgt; /* boot physical drive target */ + + int support_random_del; /* Do we support random deletion of logdrvs */ + int read_ldidmap; /* set after logical drive deltion. The logical + drive number must be read from the map */ + int quiescent; /* a stage reached when delete logical drive needs to + be done. Stop sending requests to the hba till + delete operation is completed */ + + mega_scb *int_qh; /* commands are queued in the internal queue */ + mega_scb *int_qt; /* while the hba is quiescent */ + int int_qlen; + char logdrv_chan[MAX_CHANNEL+NVIRT_CHAN]; /* logical drive are on + what channels. */ + int mega_ch_class; +} mega_host_config; + +typedef struct _driver_info { + int size; + ulong version; +} mega_driver_info; + +/* + * User ioctl structure. + * This structure will be used for Traditional Method ioctl interface + * commands (M_RD_IOCTL_CMD),Alternate Buffer Method (M_RD_IOCTL_CMD_NEW) + * ioctl commands and the Driver ioctls(M_RD_DRIVER_IOCTL_INTERFACE). + * The Driver ioctl interface handles the commands at + * the driver level, without being sent to the card. + */ +#define MEGADEVIOC 0x84 + +/* system call imposed limit. Change accordingly */ +#define IOCTL_MAX_DATALEN 4096 + +#pragma pack(1) +struct uioctl_t { + u32 inlen; + u32 outlen; + union { + u8 fca[16]; + struct { + u8 opcode; + u8 subopcode; + u16 adapno; +#if BITS_PER_LONG == 32 + u8 *buffer; + u8 pad[4]; +#endif +#if BITS_PER_LONG == 64 + u8 *buffer; +#endif + u32 length; + } fcs; + } ui; + u8 mbox[18]; /* 16 bytes + 2 status bytes */ + mega_passthru pthru; +#if BITS_PER_LONG == 32 + char *data; /* buffer <= 4096 for 0x80 commands */ + char pad[4]; +#endif +#if BITS_PER_LONG == 64 + char *data; +#endif +}; +#pragma pack() + +/* + * struct mcontroller is used to pass information about the controllers in the + * system. Its upto the application how to use the information. We are passing + * as much info about the cards as possible and useful. Before issuing the + * call to find information about the cards, the applicaiton needs to issue a + * ioctl first to find out the number of controllers in the system. + */ +#define MAX_CONTROLLERS 32 + +struct mcontroller { + u64 base; + u8 irq; + u8 numldrv; + u8 pcibus; + u16 pcidev; + u8 pcifun; + u16 pciid; + u16 pcivendor; + u8 pcislot; + u32 uid; +}; + +struct mbox_passthru { + u8 cmd; + u8 cmdid; + u16 pad1; + u32 pad2; + u32 dataxferaddr; + u8 pad3; + u8 pad4; + u8 rsvd; + u8 mboxbusy; + u8 nstatus; + u8 status; +}; + +/* + * Defines for Driver IOCTL interface, Op-code:M_RD_DRIVER_IOCTL_INTERFACE + */ +#define MEGAIOC_MAGIC 'm' +#define MEGAIOCCMD _IOWR(MEGAIOC_MAGIC, 0) /* Mega IOCTL command */ + +#define MEGAIOC_QNADAP 'm' /* Query # of adapters */ +#define MEGAIOC_QDRVRVER 'e' /* Query driver version */ +#define MEGAIOC_QADAPINFO 'g' /* Query adapter information */ +#define MKADAP(adapno) (MEGAIOC_MAGIC << 8 | (adapno) ) +#define GETADAP(mkadap) ( (mkadap) ^ MEGAIOC_MAGIC << 8 ) + +#if LINUX_VERSION_CODE < KERNEL_VERSION(2,3,0) /*0x20300 */ +extern struct proc_dir_entry proc_scsi_megaraid; +#endif + +/* For Host Re-Ordering */ +#define MAX_CONTROLLERS 32 + +struct mega_hbas { + int is_bios_enabled; + mega_host_config *hostdata_addr; +}; + +#define IS_BIOS_ENABLED 0x62 +#define GET_BIOS 0x01 +#define CHNL_CLASS 0xA9 +#define GET_CHNL_CLASS 0x00 +#define SET_CHNL_CLASS 0x01 +#define CH_RAID 0x01 +#define CH_SCSI 0x00 + + +#define BIOS_PVT_DATA 0x40 +#define GET_BIOS_PVT_DATA 0x00 + +#pragma pack(1) +struct private_bios_data { + u8 geometry:4; /* + * bits 0-3 - BIOS geometry + * 0x0001 - 1GB + * 0x0010 - 2GB + * 0x1000 - 8GB + * Others values are invalid + */ + u8 unused:4; /* bits 4-7 are unused */ + u8 boot_drv; /* + * logical/physical drive set as boot drive + * 0..7 - for 8LD cards + * 0..39 - for 40LD cards + */ + u8 rsvd[12]; + u16 cksum; /* 0-(sum of first 13 bytes of this structure) */ +}; +#pragma pack() + +/* + * Command for random deletion of logical drives + */ +#define FC_DEL_LOGDRV 0xA4 /* f/w command */ +#define OP_SUP_DEL_LOGDRV 0x2A /* is feature supported */ +#define OP_GET_LDID_MAP 0x18 /* get logdrv id and logdrv number map */ +#define OP_DEL_LOGDRV 0x1C /* delete logical drive */ + +/*================================================================ + * + * Function prototypes + * + *================================================================ + */ +const char *megaraid_info (struct Scsi_Host *); +int megaraid_detect (Scsi_Host_Template *); +int megaraid_release (struct Scsi_Host *); +int megaraid_command (Scsi_Cmnd *); +int megaraid_abort (Scsi_Cmnd *); +int megaraid_reset (Scsi_Cmnd *, unsigned int); +int megaraid_queue (Scsi_Cmnd *, void (*done) (Scsi_Cmnd *)); +int megaraid_biosparam (Disk *, kdev_t, int *); +#if 0 +int megaraid_proc_info (char *buffer, char **start, off_t offset, + int length, int hostno, int inout); +#endif +static int megaIssueCmd (mega_host_config * megaCfg, u_char * mboxData, + mega_scb * scb, int intr); +static int mega_build_sglist (mega_host_config * megaCfg, mega_scb * scb, + u32 * buffer, u32 * length); +static int mega_busyWaitMbox (mega_host_config *); +static int mega_runpendq (mega_host_config *); +static void mega_rundoneq (mega_host_config *); +static void mega_cmd_done (mega_host_config *, mega_scb *, int); +static inline void mega_freeSgList (mega_host_config * megaCfg); +static void mega_Convert8ldTo40ld (mega_RAIDINQ * inquiry, + mega_Enquiry3 * enquiry3, + megaRaidProductInfo * productInfo); + +static int megaraid_reboot_notify (struct notifier_block *, + unsigned long, void *); +#if LINUX_VERSION_CODE < KERNEL_VERSION(2,3,0) +static mega_scb *mega_ioctl (mega_host_config * megaCfg, Scsi_Cmnd * SCpnt); +static void mega_build_kernel_sg (char *barea, ulong xfersize, mega_scb * pScb, + mega_ioctl_mbox * mbox); +#endif + +static int megadev_open (struct inode *, struct file *); +static int megadev_ioctl_entry (struct inode *, struct file *, + unsigned int, unsigned long); +static int megadev_ioctl (struct inode *, struct file *, + unsigned int, unsigned long); +static mega_scb *megadev_doioctl (mega_host_config *, Scsi_Cmnd *); +static int megadev_close (struct inode *, struct file *); +static void megadev_ioctl_done (Scsi_Cmnd *); +static int mega_init_scb (mega_host_config *); +static void enq_scb_freelist (mega_host_config *, mega_scb *, + int lock, int intr); + +static int mega_is_bios_enabled (mega_host_config *); +static void mega_reorder_hosts (void); +static void mega_swap_hosts (struct Scsi_Host *, struct Scsi_Host *); + +#if 0 +static void mega_create_proc_entry (int index, struct proc_dir_entry *); +#endif +static int mega_support_ext_cdb(mega_host_config *); +static mega_passthru* mega_prepare_passthru(mega_host_config *, mega_scb *, + Scsi_Cmnd *, int, int); +static mega_ext_passthru* mega_prepare_extpassthru(mega_host_config *, + mega_scb *, Scsi_Cmnd *, int, int); +static void mega_enum_raid_scsi(mega_host_config *); +static int mega_partsize(Disk *, kdev_t, int *); +static void mega_get_boot_drv(mega_host_config *); +static int mega_get_ldrv_num(mega_host_config *, Scsi_Cmnd *, int); +static int mega_support_random_del(mega_host_config *); +static int mega_del_logdrv(mega_host_config *, int); +static int mega_do_del_logdrv(mega_host_config *, int); + +#endif + +/* vi: set ts=4: */ diff --git a/xen/include/xeno/version.h b/xen/include/xeno/version.h new file mode 100644 index 0000000000..a0bbfa972f --- /dev/null +++ b/xen/include/xeno/version.h @@ -0,0 +1,2 @@ +#define KERNEL_VERSION(a,b,c) (((a) << 16) + ((b) << 8) + (c)) +#define LINUX_VERSION_CODE KERNEL_VERSION(2,4,21) -- 2.30.2